> For the complete documentation index, see [llms.txt](https://developer.celigo.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.celigo.com/api/api-reference/data-stores/file-storage.md).

# File Storage

Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.

### Storage item schema

## The StorageItem object

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"components":{"schemas":{"StorageItem":{"type":"object","description":"A file or folder stored in Celigo Storage.","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the item. Pass it to download, move, copy,\nreplace, rename, or delete the item; folders and files share one\nID space."},"type":{"type":"string","enum":["file","folder"],"readOnly":true,"description":"Distinguishes a content-bearing file from an organizational folder.\nFixed when the item is created and never changes."},"name":{"type":"string","maxLength":256,"description":"Display name, including the extension for files (for example\n`orders.csv`). Unique among the live items of one folder: files and\nfolders share the namespace, matching is case-sensitive, and a\npending upload holds its name. Any write that would duplicate a name\nis rejected with 409 `STORAGE_ITEM_ALREADY_EXISTS`; items in the\nrecycle bin release theirs."},"_userId":{"type":"string","format":"objectId","readOnly":true,"description":"Environment user that owns the item; every item an account lists carries its own environment's value."},"_parentId":{"type":["string","null"],"format":"objectId","readOnly":true,"description":"Folder that contains this item. Null when the item sits at the\naccount root."},"_integrationId":{"type":["string","null"],"format":"objectId","readOnly":true,"description":"Integration whose system folder tree contains this item. Set on an\nintegration's root folder and inherited by everything created inside\nit; null for user-created folders and files at the account root and\ntheir descendants. Filter the list endpoint with `_integrationId` to\nscope a listing to one integration. The root folder is created with\nthe integration and follows it: deleting the integration sends the\nfolder and everything in it to the recycle bin, and purging the\nintegration purges them."},"integrationName":{"type":"string","readOnly":true,"description":"Name of the integration an integration root folder belongs to. Present\nonly on that root folder (the `isSystem` folder at the account root);\nitems inside the tree carry `_integrationId` but not this field."},"__ancestorIds":{"type":"array","readOnly":true,"description":"Folder IDs from the root down to the immediate parent, in order.\nEmpty for root-level items. Rewritten automatically when the item\nor any ancestor folder is moved.","items":{"type":"string","format":"objectId"}},"size":{"type":"integer","format":"int64","readOnly":true,"description":"File size in bytes; always `0` for folders, which hold no content.\nWhile an upload is pending this reflects the reserved quota, and is\nreconciled to the actual byte count once the upload finalizes."},"mimeType":{"type":"string","maxLength":256,"readOnly":true,"description":"Media type recorded at upload time, used to set the download\nContent-Type. Present when type is file; absent for folders and for\nfiles uploaded without a declared type. Matched case-sensitively by\nthe `mimeType` and `mimeType_nin` list filters."},"description":{"type":"string","maxLength":1000,"description":"Optional caller-supplied note describing the item."},"status":{"type":"string","enum":["pending","active"],"readOnly":true,"description":"Upload lifecycle state. A file is created pending at initiate-upload\nand flips to active once S3 confirms the bytes; folders are created\nactive."},"isSystem":{"type":"boolean","readOnly":true,"description":"When true, the platform created the item (for example a system or\nintegration folder) and protects it — rename, move, content replace,\ncopy, delete, and purge are rejected with 403\n`STORAGE_SYSTEM_ITEM_PROTECTED` (\"System items cannot be modified or\ndeleted\")."},"deletedAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the item was soft-deleted to the recycle bin. Present\nonly for recycle-bin items; the item is permanently purged 30 days\nafter this time unless restored."},"deletedBy":{"type":"string","readOnly":true,"description":"Who soft-deleted the item — a user ID for a direct delete, or\n`cascadeDelete` for a descendant removed when its parent folder was\ndeleted. Present only for recycle-bin items. Cascade-deleted items\ncan be recovered only by restoring the folder that was deleted\ndirectly."},"location":{"type":"object","readOnly":true,"description":"Folder that contains this search hit. Present only in search-mode\nresults from `GET /v1/storage/items` (when the `search` query\nparameter is set).","properties":{"_id":{"type":"string","format":"objectId","description":"ID of the containing folder."},"name":{"type":"string","description":"Name of the containing folder."}}},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the item was created."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of the most recent metadata change. Updated on rename,\nmove, and content replace, and when the folder's direct children\nchange."}},"required":["_id","_userId","type","name","status","isSystem","createdAt","lastModified"]}}}}
```

## Initiate file upload

> Starts one or more file uploads. For each file, reserves quota, creates a\
> pending item, and returns presigned S3 URLs to transfer the bytes to:\
> a single PUT URL for files up to 5 GB, or a multipart \`uploadId\`,\
> \`partUrls\`, and \`completeUrl\` for larger files. Upload the bytes directly\
> to those URLs — a single PUT must carry the\
> \`x-amz-server-side-encryption: AES256\` header — then the item flips to\
> active once S3 confirms it; no further API call is needed. Each URL's\
> lifetime is in its \`X-Amz-Expires\` parameter; once every part already\
> issued has been uploaded, a multipart upload fetches further part URLs by\
> posting again with the item's \`\_id\`, \`uploadId\`, and \`issuedPartsCount\`,\
> while an expired single PUT URL cannot be reissued.\
> Each file is validated independently, so the response can mix per-file\
> successes and failures while the request itself returns 200 — a name\
> already held by a live item in the target folder fails that entry with\
> 409 \`STORAGE\_ITEM\_ALREADY\_EXISTS\`.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"InitiateUploadRequest":{"type":"object","description":"Reserves quota and requests presigned upload URLs for one or more files.","required":["files"],"properties":{"_parentId":{"type":["string","null"],"format":"objectId","description":"Folder to create the files in. Omit or pass null to upload to the\naccount root. Must reference an existing active folder."},"files":{"type":"array","minItems":1,"description":"Files to initiate. Each entry is validated and reserved independently.","items":{"$ref":"#/components/schemas/UploadRequestFile"}}}},"UploadRequestFile":{"type":"object","description":"One file to upload within an initiate-upload request.","required":["name","uploadType"],"properties":{"_id":{"type":"string","format":"objectId","description":"Existing pending item to continue. Set it only to request the next\nbatch of part URLs for an in-progress multipart upload, alongside\n`uploadId` and `issuedPartsCount`. Omit for a new upload."},"name":{"type":"string","maxLength":256,"description":"File name, including extension. Must not match a live file or folder\nin the target folder (case-sensitive; a pending upload already holds\nits name), or the entry fails with 409 `STORAGE_ITEM_ALREADY_EXISTS`\nwhile the rest of the batch proceeds."},"mimeType":{"type":"string","maxLength":256,"description":"Media type to record for the file and serve as its download\nContent-Type. Optional; sniffed by callers from the file extension\nwhen omitted."},"size":{"type":"integer","format":"int64","minimum":0,"description":"Declared size in bytes. Used to reserve quota up front. When omitted,\nthe backend reserves a worst-case estimate (1 GB for single,\n`numParts` × 5 GB for multipart) and reconciles to the real size when\nthe upload finalizes."},"uploadType":{"type":"string","enum":["single","multipart"],"description":"How the file will be transferred. Use single for files up to 5 GB;\nuse multipart for larger files, which returns per-part URLs plus a\ncomplete URL."},"numParts":{"type":"integer","minimum":1,"description":"Required when uploadType is multipart. On a new upload, the total number\nof parts the file will be split into; the response returns presigned URLs\nfor as many parts as the part budget allows. On a resume (`_id`,\n`uploadId`, and `issuedPartsCount` set), the number of further part URLs\nto issue, numbered on from `issuedPartsCount`."},"uploadId":{"type":"string","description":"S3 multipart upload ID from a prior initiate response. Provide with\n`issuedPartsCount` (and `_id`) to resume and request the next batch\nof part URLs."},"issuedPartsCount":{"type":"integer","minimum":0,"description":"Number of part URLs already issued for this upload. Provide together\nwith `uploadId` to resume; the response continues numbering after\nthis value."}}},"InitiateUploadResponse":{"type":"object","description":"Per-file upload instructions, in the same order as the request.","required":["files"],"properties":{"files":{"type":"array","items":{"$ref":"#/components/schemas/UploadResult"}}}},"UploadResult":{"type":"object","description":"Outcome for one file in an upload response. On success, `statusCode` is\n200 and `body` carries the presigned-URL instructions; on failure,\n`statusCode` is a 4xx and `errors` explains why. Each file in a batch is\nvalidated independently, so a single response can mix successes and\nfailures.","required":["name","statusCode"],"properties":{"_id":{"type":"string","format":"objectId","description":"ID of the pending item this result refers to. Present once the item\nis created; absent when the file failed validation before creation.\nUse it to continue a multipart upload, cancel it, or download the\nfile once active."},"name":{"type":"string","description":"File name echoed back from the request, to correlate results."},"statusCode":{"type":"integer","description":"Per-file result status, mirroring HTTP status semantics."},"body":{"$ref":"#/components/schemas/UploadBody"},"errors":{"type":"array","description":"Reasons the file was rejected. Present when statusCode is a 4xx.","items":{"$ref":"#/components/schemas/UploadError"}}}},"UploadBody":{"type":"object","description":"Presigned-URL instructions for one file. The fields present depend on\n`uploadType`: a single PUT returns one `uploadUrl`; a multipart upload\nreturns an `uploadId`, a batch of `partUrls`, and a `completeUrl`.","required":["uploadType"],"properties":{"uploadType":{"type":"string","enum":["single","multipart"],"description":"Transfer mechanism the caller must use for this file."},"uploadUrl":{"type":"string","format":"uri","description":"Presigned S3 URL to PUT the file's bytes to in a single request.\nPresent when uploadType is single. The PUT must carry the header\n`x-amz-server-side-encryption: AES256` — it is part of the signature,\nand S3 rejects the upload with 403 `SignatureDoesNotMatch` without\nit. The URL's `X-Amz-Expires` parameter carries its lifetime and\nthere is no endpoint to reissue it: an expired single PUT means\nstarting a new upload. While the URL is live the pending item holds\nits name and cannot be deleted (`STORAGE_PRESIGNED_URL_ACTIVE`)."},"uploadId":{"type":"string","description":"S3 multipart upload identifier. Present when uploadType is\nmultipart. Send it back to `POST /v1/storage/files/initiateUpload`\nwith the item's `_id` and `issuedPartsCount` to fetch the next batch\nof part URLs once every part already issued has been uploaded."},"partUrls":{"type":"array","description":"Presigned URLs for the next batch of parts. Present when uploadType\nis multipart. A request may return fewer parts than requested when\nthe account-wide part budget is constrained; after uploading every\npart in this batch, fetch the remaining parts by posting to\ninitiateUpload again with the item's `_id`, `uploadId`, and\n`issuedPartsCount` — before then the entry fails with\n`STORAGE_PARTS_MISMATCH`.","items":{"$ref":"#/components/schemas/PartUrl"}},"completeUrl":{"type":"string","format":"uri","description":"Presigned URL to POST the ordered list of part numbers and ETags to,\nfinalizing the multipart upload. Present when uploadType is multipart."}}},"PartUrl":{"type":"object","description":"A presigned URL for uploading one part of a multipart upload.","required":["partNumber","url"],"properties":{"partNumber":{"type":"integer","minimum":1,"description":"1-based index of this part. Send it back as the S3 `partNumber`\nwhen uploading, and keep each part's returned `ETag` to finalize\nthe upload."},"url":{"type":"string","format":"uri","description":"Presigned S3 URL to PUT this part's bytes to."}}},"UploadError":{"type":"object","description":"A per-file failure within a batch upload response.","required":["code","message"],"properties":{"code":{"type":"string","description":"Machine-readable storage error code."},"message":{"type":"string","description":"Human-readable explanation of the failure."}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/storage/files/initiateUpload":{"post":{"summary":"Initiate file upload","description":"Starts one or more file uploads. For each file, reserves quota, creates a\npending item, and returns presigned S3 URLs to transfer the bytes to:\na single PUT URL for files up to 5 GB, or a multipart `uploadId`,\n`partUrls`, and `completeUrl` for larger files. Upload the bytes directly\nto those URLs — a single PUT must carry the\n`x-amz-server-side-encryption: AES256` header — then the item flips to\nactive once S3 confirms it; no further API call is needed. Each URL's\nlifetime is in its `X-Amz-Expires` parameter; once every part already\nissued has been uploaded, a multipart upload fetches further part URLs by\nposting again with the item's `_id`, `uploadId`, and `issuedPartsCount`,\nwhile an expired single PUT URL cannot be reissued.\nEach file is validated independently, so the response can mix per-file\nsuccesses and failures while the request itself returns 200 — a name\nalready held by a live item in the target folder fails that entry with\n409 `STORAGE_ITEM_ALREADY_EXISTS`.","operationId":"initiateStorageUpload","tags":["File Storage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InitiateUploadRequest"}}}},"responses":{"200":{"description":"Per-file upload instructions, in request order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InitiateUploadResponse"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Cancel a multipart upload

> Aborts an in-progress multipart upload, discarding any uploaded parts and\
> releasing the reserved quota. Use this to abandon a multipart upload\
> started with \`POST /v1/storage/files/initiateUpload\`; single PUT uploads\
> do not need canceling. If the upload has in fact already completed in S3,\
> the item is activated instead and the request is rejected as not pending.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}}},"paths":{"/v1/storage/files/{_id}/cancel":{"post":{"summary":"Cancel a multipart upload","description":"Aborts an in-progress multipart upload, discarding any uploaded parts and\nreleasing the reserved quota. Use this to abandon a multipart upload\nstarted with `POST /v1/storage/files/initiateUpload`; single PUT uploads\ndo not need canceling. If the upload has in fact already completed in S3,\nthe item is activated instead and the request is rejected as not pending.","operationId":"cancelStorageUpload","tags":["File Storage"],"parameters":[{"name":"_id","in":"path","required":true,"description":"ID of the pending multipart item to cancel.","schema":{"type":"string","format":"objectId"}}],"responses":{"200":{"description":"Multipart upload aborted and the pending item removed. Returns the\nremoved item's `_id` and `name`; canceling it again answers 404\n`STORAGE_ITEM_NOT_FOUND`.","content":{"application/json":{"schema":{"type":"object","required":["_id","name"],"properties":{"_id":{"type":"string","format":"objectId","description":"ID of the canceled item."},"name":{"type":"string","description":"Name of the canceled item."}}}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Get a file download URL

> Returns a short-lived presigned S3 URL for downloading a file's content,\
> along with how many seconds it stays valid. Fetch the bytes directly from\
> that URL — the content does not pass through this API. Files only; folders\
> have no content to download.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}}},"paths":{"/v1/storage/files/{_id}/download":{"get":{"summary":"Get a file download URL","description":"Returns a short-lived presigned S3 URL for downloading a file's content,\nalong with how many seconds it stays valid. Fetch the bytes directly from\nthat URL — the content does not pass through this API. Files only; folders\nhave no content to download.","operationId":"getStorageFileDownloadUrl","tags":["File Storage"],"parameters":[{"name":"_id","in":"path","required":true,"description":"ID of the file to download.","schema":{"type":"string","format":"objectId"}}],"responses":{"200":{"description":"A presigned download URL and its lifetime.","content":{"application/json":{"schema":{"type":"object","required":["downloadUrl","expiresIn"],"properties":{"downloadUrl":{"type":"string","format":"uri","description":"Presigned S3 URL to GET the file's bytes from."},"expiresIn":{"type":"integer","description":"Seconds the URL stays valid from the time of this response."}}}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Get a folder download URL

> Packages a folder and everything beneath it into a zip and returns a\
> short-lived presigned S3 URL to fetch it from — the content does not pass\
> through this API. The zip is built synchronously before the response, so\
> large trees take longer to answer; entries are rooted at the folder's own\
> name and every subfolder is written as a directory entry, so empty\
> subfolders survive extraction. Pending (not yet finalized) files are\
> skipped. Re-downloading the same folder overwrites its previously staged\
> zip rather than piling up copies. Folders only; download a single file\
> with \`GET /v1/storage/files/{\_id}/download\`.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}}},"paths":{"/v1/storage/folders/{_id}/download":{"get":{"summary":"Get a folder download URL","description":"Packages a folder and everything beneath it into a zip and returns a\nshort-lived presigned S3 URL to fetch it from — the content does not pass\nthrough this API. The zip is built synchronously before the response, so\nlarge trees take longer to answer; entries are rooted at the folder's own\nname and every subfolder is written as a directory entry, so empty\nsubfolders survive extraction. Pending (not yet finalized) files are\nskipped. Re-downloading the same folder overwrites its previously staged\nzip rather than piling up copies. Folders only; download a single file\nwith `GET /v1/storage/files/{_id}/download`.","operationId":"getStorageFolderDownloadUrl","tags":["File Storage"],"parameters":[{"name":"_id","in":"path","required":true,"description":"ID of the folder to download.","schema":{"type":"string","format":"objectId"}}],"responses":{"200":{"description":"A presigned download URL for the zip, its lifetime, and what it contains.","content":{"application/json":{"schema":{"type":"object","required":["downloadUrl","expiresIn","name","fileCount","size"],"properties":{"downloadUrl":{"type":"string","format":"uri","description":"Presigned S3 URL to GET the zip from. Serves the archive with\na `Content-Disposition: attachment` header carrying `name`."},"expiresIn":{"type":"integer","description":"Seconds the URL stays valid from the time of this response."},"name":{"type":"string","description":"File name of the zip — the folder's name with characters that\nare illegal in file names replaced by `_`, plus `.zip`."},"fileCount":{"type":"integer","description":"Number of files packaged into the zip. Folders are not counted."},"size":{"type":"integer","format":"int64","description":"Total uncompressed size in bytes of the packaged files."}}}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"description":"No folder or file with this ID exists in the environment. Error code `STORAGE_ITEM_NOT_FOUND`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The ID belongs to a file, not a folder. Error code `STORAGE_NOT_A_FOLDER`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## List or search items

> Browse or search Celigo Storage. In list mode (no \`search\`), returns the\
> direct children of a folder — omit \`\_parentId\` for the account root — and\
> includes a \`breadcrumb\` of the path to that folder. In search mode (with\
> \`search\`), recursively matches item names across the whole account and\
> returns each hit's \`location\`. Results are paginated with the \`Link\`\
> response header.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"StorageItem":{"type":"object","description":"A file or folder stored in Celigo Storage.","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the item. Pass it to download, move, copy,\nreplace, rename, or delete the item; folders and files share one\nID space."},"type":{"type":"string","enum":["file","folder"],"readOnly":true,"description":"Distinguishes a content-bearing file from an organizational folder.\nFixed when the item is created and never changes."},"name":{"type":"string","maxLength":256,"description":"Display name, including the extension for files (for example\n`orders.csv`). Unique among the live items of one folder: files and\nfolders share the namespace, matching is case-sensitive, and a\npending upload holds its name. Any write that would duplicate a name\nis rejected with 409 `STORAGE_ITEM_ALREADY_EXISTS`; items in the\nrecycle bin release theirs."},"_userId":{"type":"string","format":"objectId","readOnly":true,"description":"Environment user that owns the item; every item an account lists carries its own environment's value."},"_parentId":{"type":["string","null"],"format":"objectId","readOnly":true,"description":"Folder that contains this item. Null when the item sits at the\naccount root."},"_integrationId":{"type":["string","null"],"format":"objectId","readOnly":true,"description":"Integration whose system folder tree contains this item. Set on an\nintegration's root folder and inherited by everything created inside\nit; null for user-created folders and files at the account root and\ntheir descendants. Filter the list endpoint with `_integrationId` to\nscope a listing to one integration. The root folder is created with\nthe integration and follows it: deleting the integration sends the\nfolder and everything in it to the recycle bin, and purging the\nintegration purges them."},"integrationName":{"type":"string","readOnly":true,"description":"Name of the integration an integration root folder belongs to. Present\nonly on that root folder (the `isSystem` folder at the account root);\nitems inside the tree carry `_integrationId` but not this field."},"__ancestorIds":{"type":"array","readOnly":true,"description":"Folder IDs from the root down to the immediate parent, in order.\nEmpty for root-level items. Rewritten automatically when the item\nor any ancestor folder is moved.","items":{"type":"string","format":"objectId"}},"size":{"type":"integer","format":"int64","readOnly":true,"description":"File size in bytes; always `0` for folders, which hold no content.\nWhile an upload is pending this reflects the reserved quota, and is\nreconciled to the actual byte count once the upload finalizes."},"mimeType":{"type":"string","maxLength":256,"readOnly":true,"description":"Media type recorded at upload time, used to set the download\nContent-Type. Present when type is file; absent for folders and for\nfiles uploaded without a declared type. Matched case-sensitively by\nthe `mimeType` and `mimeType_nin` list filters."},"description":{"type":"string","maxLength":1000,"description":"Optional caller-supplied note describing the item."},"status":{"type":"string","enum":["pending","active"],"readOnly":true,"description":"Upload lifecycle state. A file is created pending at initiate-upload\nand flips to active once S3 confirms the bytes; folders are created\nactive."},"isSystem":{"type":"boolean","readOnly":true,"description":"When true, the platform created the item (for example a system or\nintegration folder) and protects it — rename, move, content replace,\ncopy, delete, and purge are rejected with 403\n`STORAGE_SYSTEM_ITEM_PROTECTED` (\"System items cannot be modified or\ndeleted\")."},"deletedAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the item was soft-deleted to the recycle bin. Present\nonly for recycle-bin items; the item is permanently purged 30 days\nafter this time unless restored."},"deletedBy":{"type":"string","readOnly":true,"description":"Who soft-deleted the item — a user ID for a direct delete, or\n`cascadeDelete` for a descendant removed when its parent folder was\ndeleted. Present only for recycle-bin items. Cascade-deleted items\ncan be recovered only by restoring the folder that was deleted\ndirectly."},"location":{"type":"object","readOnly":true,"description":"Folder that contains this search hit. Present only in search-mode\nresults from `GET /v1/storage/items` (when the `search` query\nparameter is set).","properties":{"_id":{"type":"string","format":"objectId","description":"ID of the containing folder."},"name":{"type":"string","description":"Name of the containing folder."}}},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the item was created."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of the most recent metadata change. Updated on rename,\nmove, and content replace, and when the folder's direct children\nchange."}},"required":["_id","_userId","type","name","status","isSystem","createdAt","lastModified"]},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/storage/items":{"get":{"summary":"List or search items","description":"Browse or search Celigo Storage. In list mode (no `search`), returns the\ndirect children of a folder — omit `_parentId` for the account root — and\nincludes a `breadcrumb` of the path to that folder. In search mode (with\n`search`), recursively matches item names across the whole account and\nreturns each hit's `location`. Results are paginated with the `Link`\nresponse header.","operationId":"listStorageItems","tags":["File Storage"],"parameters":[{"name":"_parentId","in":"query","required":false,"description":"Folder whose direct children to list. Omit for the account root.\nIgnored in search mode. An id that names no item answers 404\n`STORAGE_PARENT_NOT_FOUND`; a file's id answers 422\n`STORAGE_PARENT_NOT_FOLDER`.","schema":{"type":"string","format":"objectId"}},{"name":"search","in":"query","required":false,"description":"Recursive, case-insensitive match on item names across the whole\naccount. Switches the endpoint to search mode, where `_parentId` is\nignored and each item carries its `location`. Matches names only, not\ncontents or descriptions; a query of several words matches names that\ncontain any of them, and hyphens separate words too, so\n`orders-2026-q3` also matches every name containing `orders`. To find\none item by its exact name, list its folder with `name`. Cannot be\ncombined with `name`, `prefix`, or `_integrationId`.","schema":{"type":"string","minLength":2}},{"name":"name","in":"query","required":false,"description":"Return only items whose name equals one of the given values —\ncase-sensitive, exact match. Repeat the parameter for several names;\na comma inside a value is part of the name, not a separator. Cannot\nbe combined with `search` or `prefix`.","schema":{"type":"array","items":{"type":"string","maxLength":256}}},{"name":"prefix","in":"query","required":false,"description":"Return only items whose name starts with this value (case-sensitive).\nA single value — cannot be combined with `search` or `name`.","schema":{"type":"string","minLength":1,"maxLength":256}},{"name":"_integrationId","in":"query","required":false,"description":"Return only items that belong to this integration's system folder\ntree. At the account root this yields the integration's root folder;\nwith `_parentId` set to a folder inside the tree it narrows that\nfolder's children. Cannot be combined with `search`.","schema":{"type":"string","format":"objectId"}},{"name":"type","in":"query","required":false,"description":"Restrict results to one item type.","schema":{"type":"string","enum":["file","folder"]}},{"name":"mimeType","in":"query","required":false,"description":"Return only files whose media type is one of the given values —\ncase-sensitive, exact match, so `image/*` is rejected. Pass several\nvalues as a comma-separated list or by repeating the parameter; up to\n50 distinct values per request. Folders never match.","schema":{"type":"array","items":{"type":"string"},"maxItems":50}},{"name":"mimeType_nin","in":"query","required":false,"description":"Exclude files whose media type is one of the given values — the\n\"Other\" filter. Same list syntax and 50-value cap as `mimeType`.\nFolders carry no media type, so they are never excluded; add\n`type=file` to drop them. Combined with `mimeType`, both conditions\napply.","schema":{"type":"array","items":{"type":"string"},"maxItems":50}},{"name":"sort_by","in":"query","required":false,"description":"Field to sort by.","schema":{"type":"string","enum":["name","lastModified"],"default":"name"}},{"name":"sort_order","in":"query","required":false,"description":"Sort direction.","schema":{"type":"string","enum":["asc","desc"],"default":"asc"}},{"name":"lastModified_gte","in":"query","required":false,"description":"Return only items modified at or after this time.","schema":{"type":"string","format":"date-time"}},{"name":"lastModified_lte","in":"query","required":false,"description":"Return only items modified at or before this time.","schema":{"type":"string","format":"date-time"}},{"name":"pageSize","in":"query","required":false,"description":"Maximum number of items to return per page.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":1000}},{"name":"after","in":"query","required":false,"description":"Opaque cursor for the next page. Take it from the `Link` response\nheader's `rel=\"next\"` URL rather than constructing it. Cannot be\ncombined with `before`.","schema":{"type":"string"}},{"name":"before","in":"query","required":false,"description":"Opaque cursor for the previous page, taken from the `Link` header's\n`rel=\"prev\"` URL. Cannot be combined with `after`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Matching items. Includes `breadcrumb` in list mode and per-item\n`location` in search mode.","headers":{"Link":{"description":"RFC 5988 pagination links. Includes `<...>; rel=\"next\"` (and\n`rel=\"prev\"` when paging backward) with cursor params; the\n`rel=\"next\"` entry is absent on the final page.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/StorageItem"}},"breadcrumb":{"type":"array","description":"Path from the root to the listed folder, nearest the root\nfirst. Present in list mode when `_parentId` is set.","items":{"type":"object","properties":{"_id":{"type":"string","format":"objectId","description":"Folder ID."},"name":{"type":"string","description":"Folder name."}}}}}}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"description":"`_parentId` names no item in the account. Error code:\n`STORAGE_PARENT_NOT_FOUND`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"A filter is malformed or two filters that cannot be combined were\nsent together (`STORAGE_INVALID_FILTER`), or `_parentId` is a file\nrather than a folder (`STORAGE_PARENT_NOT_FOLDER`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Create a folder

> Creates an empty folder. Folders are organizational only — they hold no\
> content and do not count against storage quota. To create files, use\
> \`POST /v1/storage/files/initiateUpload\`. A name already held by a live\
> file or folder in the parent is rejected with 409\
> \`STORAGE\_ITEM\_ALREADY\_EXISTS\`.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"CreateFolderRequest":{"type":"object","description":"Request body for creating a folder.","required":["name"],"properties":{"name":{"type":"string","maxLength":256,"description":"Folder name. Must not match a live file or folder already in the\nparent (case-sensitive); a duplicate is rejected with 409\n`STORAGE_ITEM_ALREADY_EXISTS`."},"_parentId":{"type":["string","null"],"format":"objectId","description":"Parent folder to create this folder in. Omit or pass null to create\nit at the account root. Must reference an existing active folder."},"description":{"type":"string","maxLength":1000,"description":"Optional note describing the folder."}}},"StorageItem":{"type":"object","description":"A file or folder stored in Celigo Storage.","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the item. Pass it to download, move, copy,\nreplace, rename, or delete the item; folders and files share one\nID space."},"type":{"type":"string","enum":["file","folder"],"readOnly":true,"description":"Distinguishes a content-bearing file from an organizational folder.\nFixed when the item is created and never changes."},"name":{"type":"string","maxLength":256,"description":"Display name, including the extension for files (for example\n`orders.csv`). Unique among the live items of one folder: files and\nfolders share the namespace, matching is case-sensitive, and a\npending upload holds its name. Any write that would duplicate a name\nis rejected with 409 `STORAGE_ITEM_ALREADY_EXISTS`; items in the\nrecycle bin release theirs."},"_userId":{"type":"string","format":"objectId","readOnly":true,"description":"Environment user that owns the item; every item an account lists carries its own environment's value."},"_parentId":{"type":["string","null"],"format":"objectId","readOnly":true,"description":"Folder that contains this item. Null when the item sits at the\naccount root."},"_integrationId":{"type":["string","null"],"format":"objectId","readOnly":true,"description":"Integration whose system folder tree contains this item. Set on an\nintegration's root folder and inherited by everything created inside\nit; null for user-created folders and files at the account root and\ntheir descendants. Filter the list endpoint with `_integrationId` to\nscope a listing to one integration. The root folder is created with\nthe integration and follows it: deleting the integration sends the\nfolder and everything in it to the recycle bin, and purging the\nintegration purges them."},"integrationName":{"type":"string","readOnly":true,"description":"Name of the integration an integration root folder belongs to. Present\nonly on that root folder (the `isSystem` folder at the account root);\nitems inside the tree carry `_integrationId` but not this field."},"__ancestorIds":{"type":"array","readOnly":true,"description":"Folder IDs from the root down to the immediate parent, in order.\nEmpty for root-level items. Rewritten automatically when the item\nor any ancestor folder is moved.","items":{"type":"string","format":"objectId"}},"size":{"type":"integer","format":"int64","readOnly":true,"description":"File size in bytes; always `0` for folders, which hold no content.\nWhile an upload is pending this reflects the reserved quota, and is\nreconciled to the actual byte count once the upload finalizes."},"mimeType":{"type":"string","maxLength":256,"readOnly":true,"description":"Media type recorded at upload time, used to set the download\nContent-Type. Present when type is file; absent for folders and for\nfiles uploaded without a declared type. Matched case-sensitively by\nthe `mimeType` and `mimeType_nin` list filters."},"description":{"type":"string","maxLength":1000,"description":"Optional caller-supplied note describing the item."},"status":{"type":"string","enum":["pending","active"],"readOnly":true,"description":"Upload lifecycle state. A file is created pending at initiate-upload\nand flips to active once S3 confirms the bytes; folders are created\nactive."},"isSystem":{"type":"boolean","readOnly":true,"description":"When true, the platform created the item (for example a system or\nintegration folder) and protects it — rename, move, content replace,\ncopy, delete, and purge are rejected with 403\n`STORAGE_SYSTEM_ITEM_PROTECTED` (\"System items cannot be modified or\ndeleted\")."},"deletedAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the item was soft-deleted to the recycle bin. Present\nonly for recycle-bin items; the item is permanently purged 30 days\nafter this time unless restored."},"deletedBy":{"type":"string","readOnly":true,"description":"Who soft-deleted the item — a user ID for a direct delete, or\n`cascadeDelete` for a descendant removed when its parent folder was\ndeleted. Present only for recycle-bin items. Cascade-deleted items\ncan be recovered only by restoring the folder that was deleted\ndirectly."},"location":{"type":"object","readOnly":true,"description":"Folder that contains this search hit. Present only in search-mode\nresults from `GET /v1/storage/items` (when the `search` query\nparameter is set).","properties":{"_id":{"type":"string","format":"objectId","description":"ID of the containing folder."},"name":{"type":"string","description":"Name of the containing folder."}}},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the item was created."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of the most recent metadata change. Updated on rename,\nmove, and content replace, and when the folder's direct children\nchange."}},"required":["_id","_userId","type","name","status","isSystem","createdAt","lastModified"]},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/storage/items":{"post":{"summary":"Create a folder","description":"Creates an empty folder. Folders are organizational only — they hold no\ncontent and do not count against storage quota. To create files, use\n`POST /v1/storage/files/initiateUpload`. A name already held by a live\nfile or folder in the parent is rejected with 409\n`STORAGE_ITEM_ALREADY_EXISTS`.","operationId":"createStorageFolder","tags":["File Storage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFolderRequest"}}}},"responses":{"201":{"description":"Created folder.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorageItem"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"},"409":{"description":"A live file or folder with this name already exists in the parent.\nError code: `STORAGE_ITEM_ALREADY_EXISTS`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Look up items in batch

> Returns metadata for up to 100 items in one call. Send the item ids and\
> optionally a \`fields\` projection; by default each result carries only\
> \`\_id\`. Requesting \`path\` adds a breadcrumb path built from the item's\
> ancestor folder names.\
> \
> With \`failFast: true\` (the default), the first unknown id fails the whole\
> request with \`422 STORAGE\_BATCH\_ITEM\_NOT\_FOUND\`. With \`failFast: false\`,\
> unknown ids come back inline as\
> \`{\_id, notFound: true, reason: "STORAGE\_BATCH\_ITEM\_NOT\_FOUND"}\` entries\
> alongside the found items.\
> \
> Only files are looked up: a folder's id is treated as unknown, so it\
> fails the request or comes back as a \`notFound\` entry. Read folder\
> metadata from \`GET /v1/storage/items\`.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}}}},"paths":{"/v1/storage/items/batch":{"post":{"summary":"Look up items in batch","description":"Returns metadata for up to 100 items in one call. Send the item ids and\noptionally a `fields` projection; by default each result carries only\n`_id`. Requesting `path` adds a breadcrumb path built from the item's\nancestor folder names.\n\nWith `failFast: true` (the default), the first unknown id fails the whole\nrequest with `422 STORAGE_BATCH_ITEM_NOT_FOUND`. With `failFast: false`,\nunknown ids come back inline as\n`{_id, notFound: true, reason: \"STORAGE_BATCH_ITEM_NOT_FOUND\"}` entries\nalongside the found items.\n\nOnly files are looked up: a folder's id is treated as unknown, so it\nfails the request or comes back as a `notFound` entry. Read folder\nmetadata from `GET /v1/storage/items`.","operationId":"batchGetStorageItems","tags":["File Storage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ids"],"properties":{"ids":{"type":"array","minItems":1,"maxItems":100,"description":"Item ids to look up. Each must be a 24-character ObjectId;\na malformed id fails the request with\n`400 STORAGE_BATCH_INVALID_REQUEST` before any lookup runs.","items":{"type":"string","format":"objectId"}},"fields":{"type":"array","minItems":1,"description":"Which fields to return per item (`_id` is always included).\nOmitted means `_id` only. An unknown field name fails with\n`400 STORAGE_BATCH_INVALID_REQUEST` naming the allowed set.","items":{"type":"string","enum":["name","description","size","mimeType","type","status","_parentId","__ancestorIds","path"]}},"failFast":{"type":"boolean","default":true,"description":"When true (the default), an unknown id fails the whole request\nwith 422. When false, unknown ids are reported inline per item\nand the request still returns 200."}}}}}},"responses":{"200":{"description":"Per-id results, in the same order as the requested `ids`.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","description":"One entry per requested id, in request order.","items":{"type":"object","required":["_id"],"properties":{"_id":{"type":"string","format":"objectId","description":"Item id this entry corresponds to."},"name":{"type":"string","description":"Present when requested via `fields`."},"size":{"type":"integer","description":"File size in bytes. Present when requested via `fields`."},"path":{"type":"string","description":"Breadcrumb path from the root, built from ancestor\nfolder names. Present when requested via `fields`."},"notFound":{"type":"boolean","description":"Present (true) only on unknown ids when\n`failFast: false`."},"reason":{"type":"string","description":"Error code for an unknown id when `failFast: false`\n(`STORAGE_BATCH_ITEM_NOT_FOUND`)."}}}}}}}}},"400":{"description":"Malformed request — `ids` missing/empty/over 100 entries, a\nnon-ObjectId id, an unknown `fields` entry, or a non-boolean\n`failFast`. Error code: `STORAGE_BATCH_INVALID_REQUEST`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"422":{"description":"An id was not found while `failFast` was true (the default). Each\nerror entry carries the offending `_id`.","content":{"application/json":{"schema":{"type":"object","properties":{"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Error code (`STORAGE_BATCH_ITEM_NOT_FOUND`)."},"message":{"type":"string","description":"Human-readable error message naming the id."},"_id":{"type":"string","format":"objectId","description":"The id that was not found."}}}}}}}}}}}}}}
```

## Move items in bulk

> Moves up to 100 files or folders into one destination folder (or the\
> account root) in a single call. Metadata only — content stays in place,\
> ids are unchanged, and quota is unaffected. Folders re-parent immediately\
> and re-anchor their descendants asynchronously.\
> \
> The destination is validated once up front — a bad destination rejects the\
> whole batch. Each source id is then processed best-effort: the response is\
> \`200\` when every item moved, or \`207\` when results are mixed, with a\
> per-id \`status\` of \`success\` or \`error\`. An optional \`name\` renames the\
> item as it lands (single-id requests only) so a destination name collision\
> can be resolved without a second call.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"BulkMoveResults":{"type":"object","description":"Per-id move results, one entry per source id.","properties":{"results":{"type":"array","items":{"type":"object","required":["_id","status"],"properties":{"_id":{"type":"string","format":"objectId","description":"Source item id this entry corresponds to."},"status":{"type":"string","enum":["success","error"],"description":"Outcome for this item."},"_parentId":{"type":["string","null"],"format":"objectId","description":"New parent folder (null for the root). Present on success."},"__ancestorIds":{"type":"array","description":"New ancestor chain from the root. Present on success.","items":{"type":"string","format":"objectId"}},"_integrationId":{"type":["string","null"],"format":"objectId","description":"The item's integration after the move — the destination's\nintegration when it landed inside an integration's system folder\ntree, null when it landed outside one. Present on success."},"name":{"type":"string","description":"Name of the moved item (the new name when `name` was sent). Present on success."},"type":{"type":"string","enum":["file","folder"],"description":"Type of the moved item. Present on success."},"errorCode":{"type":"string","description":"Error code for this item, such as `STORAGE_ITEM_ALREADY_EXISTS`\nwhen the destination already holds a live item with that name.\nPresent on error."},"message":{"type":"string","description":"Human-readable failure reason. Present on error."}}}}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/storage/items/move":{"post":{"summary":"Move items in bulk","description":"Moves up to 100 files or folders into one destination folder (or the\naccount root) in a single call. Metadata only — content stays in place,\nids are unchanged, and quota is unaffected. Folders re-parent immediately\nand re-anchor their descendants asynchronously.\n\nThe destination is validated once up front — a bad destination rejects the\nwhole batch. Each source id is then processed best-effort: the response is\n`200` when every item moved, or `207` when results are mixed, with a\nper-id `status` of `success` or `error`. An optional `name` renames the\nitem as it lands (single-id requests only) so a destination name collision\ncan be resolved without a second call.","operationId":"bulkMoveStorageItems","tags":["File Storage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["_parentFolderId","_storageItemIds"],"properties":{"_parentFolderId":{"type":["string","null"],"format":"objectId","description":"Destination folder. Send null to move the items to the account\nroot. The key must be present — omitting it fails with\n`400 STORAGE_BULK_MOVE_PARENT_REQUIRED`. Must not appear in\n`_storageItemIds` (`422 STORAGE_BULK_MOVE_DESTINATION_IN_SOURCES`)."},"_storageItemIds":{"type":"array","minItems":1,"maxItems":100,"description":"Ids of the items to move. Over 100 entries fails with\n`422 STORAGE_BULK_MOVE_TOO_MANY_ITEMS`.","items":{"type":"string","format":"objectId"}},"name":{"type":"string","description":"New name applied to the moved item in the same write as the\nre-parent. Only allowed when `_storageItemIds` has exactly one\nentry — with more, the request fails with\n`400 STORAGE_BULK_MOVE_NAME_DISALLOWED`."}}}}}},"responses":{"200":{"description":"Every item moved successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkMoveResults"}}}},"207":{"description":"Mixed results — at least one item failed to move. Inspect each\nentry's `status`; a destination name collision reports\n`STORAGE_ITEM_ALREADY_EXISTS` on that entry while the others proceed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkMoveResults"}}}},"400":{"description":"Malformed request — `_parentFolderId` key missing\n(`STORAGE_BULK_MOVE_PARENT_REQUIRED`), `_storageItemIds`\nmissing or empty (`STORAGE_BULK_MOVE_IDS_REQUIRED`), or `name` sent\nwith more than one id (`STORAGE_BULK_MOVE_NAME_DISALLOWED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"description":"Batch-level rejection — too many ids\n(`STORAGE_BULK_MOVE_TOO_MANY_ITEMS`), the destination appears among\nthe source ids (`STORAGE_BULK_MOVE_DESTINATION_IN_SOURCES`), or the\ndestination is not a folder.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## List recycle-bin items

> Lists the items currently in the recycle bin, newest deletion first. Only\
> items deleted directly are listed — descendants removed as part of a\
> folder deletion are hidden, since they are restored or purged with their\
> parent. Each item carries \`deletedAt\` and \`deletedBy\`. Restore an item\
> with \`POST /v1/storage/items/{\_id}/restore\` or remove it for good with\
> \`DELETE /v1/storage/items/{\_id}/purge\`.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"StorageItem":{"type":"object","description":"A file or folder stored in Celigo Storage.","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the item. Pass it to download, move, copy,\nreplace, rename, or delete the item; folders and files share one\nID space."},"type":{"type":"string","enum":["file","folder"],"readOnly":true,"description":"Distinguishes a content-bearing file from an organizational folder.\nFixed when the item is created and never changes."},"name":{"type":"string","maxLength":256,"description":"Display name, including the extension for files (for example\n`orders.csv`). Unique among the live items of one folder: files and\nfolders share the namespace, matching is case-sensitive, and a\npending upload holds its name. Any write that would duplicate a name\nis rejected with 409 `STORAGE_ITEM_ALREADY_EXISTS`; items in the\nrecycle bin release theirs."},"_userId":{"type":"string","format":"objectId","readOnly":true,"description":"Environment user that owns the item; every item an account lists carries its own environment's value."},"_parentId":{"type":["string","null"],"format":"objectId","readOnly":true,"description":"Folder that contains this item. Null when the item sits at the\naccount root."},"_integrationId":{"type":["string","null"],"format":"objectId","readOnly":true,"description":"Integration whose system folder tree contains this item. Set on an\nintegration's root folder and inherited by everything created inside\nit; null for user-created folders and files at the account root and\ntheir descendants. Filter the list endpoint with `_integrationId` to\nscope a listing to one integration. The root folder is created with\nthe integration and follows it: deleting the integration sends the\nfolder and everything in it to the recycle bin, and purging the\nintegration purges them."},"integrationName":{"type":"string","readOnly":true,"description":"Name of the integration an integration root folder belongs to. Present\nonly on that root folder (the `isSystem` folder at the account root);\nitems inside the tree carry `_integrationId` but not this field."},"__ancestorIds":{"type":"array","readOnly":true,"description":"Folder IDs from the root down to the immediate parent, in order.\nEmpty for root-level items. Rewritten automatically when the item\nor any ancestor folder is moved.","items":{"type":"string","format":"objectId"}},"size":{"type":"integer","format":"int64","readOnly":true,"description":"File size in bytes; always `0` for folders, which hold no content.\nWhile an upload is pending this reflects the reserved quota, and is\nreconciled to the actual byte count once the upload finalizes."},"mimeType":{"type":"string","maxLength":256,"readOnly":true,"description":"Media type recorded at upload time, used to set the download\nContent-Type. Present when type is file; absent for folders and for\nfiles uploaded without a declared type. Matched case-sensitively by\nthe `mimeType` and `mimeType_nin` list filters."},"description":{"type":"string","maxLength":1000,"description":"Optional caller-supplied note describing the item."},"status":{"type":"string","enum":["pending","active"],"readOnly":true,"description":"Upload lifecycle state. A file is created pending at initiate-upload\nand flips to active once S3 confirms the bytes; folders are created\nactive."},"isSystem":{"type":"boolean","readOnly":true,"description":"When true, the platform created the item (for example a system or\nintegration folder) and protects it — rename, move, content replace,\ncopy, delete, and purge are rejected with 403\n`STORAGE_SYSTEM_ITEM_PROTECTED` (\"System items cannot be modified or\ndeleted\")."},"deletedAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the item was soft-deleted to the recycle bin. Present\nonly for recycle-bin items; the item is permanently purged 30 days\nafter this time unless restored."},"deletedBy":{"type":"string","readOnly":true,"description":"Who soft-deleted the item — a user ID for a direct delete, or\n`cascadeDelete` for a descendant removed when its parent folder was\ndeleted. Present only for recycle-bin items. Cascade-deleted items\ncan be recovered only by restoring the folder that was deleted\ndirectly."},"location":{"type":"object","readOnly":true,"description":"Folder that contains this search hit. Present only in search-mode\nresults from `GET /v1/storage/items` (when the `search` query\nparameter is set).","properties":{"_id":{"type":"string","format":"objectId","description":"ID of the containing folder."},"name":{"type":"string","description":"Name of the containing folder."}}},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the item was created."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of the most recent metadata change. Updated on rename,\nmove, and content replace, and when the folder's direct children\nchange."}},"required":["_id","_userId","type","name","status","isSystem","createdAt","lastModified"]},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/storage/items/recycleBinTTL":{"get":{"summary":"List recycle-bin items","description":"Lists the items currently in the recycle bin, newest deletion first. Only\nitems deleted directly are listed — descendants removed as part of a\nfolder deletion are hidden, since they are restored or purged with their\nparent. Each item carries `deletedAt` and `deletedBy`. Restore an item\nwith `POST /v1/storage/items/{_id}/restore` or remove it for good with\n`DELETE /v1/storage/items/{_id}/purge`.","operationId":"listStorageRecycleBin","tags":["File Storage"],"parameters":[{"name":"pageSize","in":"query","required":false,"description":"Maximum number of items to return per page.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":1000}},{"name":"after","in":"query","required":false,"description":"Opaque forward-paging cursor. Take it from the previous response's\n`Link` header (`rel=\"next\"`); omit for the first page. Cannot be\ncombined with `before`.","schema":{"type":"string"}},{"name":"before","in":"query","required":false,"description":"Opaque backward-paging cursor from a previous response's `Link`\nheader. Cannot be combined with `after`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Recycle-bin items. Pagination is cursor-based via the `Link`\nheader — the body carries only `items`.","headers":{"Link":{"description":"RFC-5988 pagination links. When more pages remain, includes a\n`<...?after=...>; rel=\"next\"` entry; absent on the final page.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/StorageItem"}}}}}}},"400":{"description":"The `after`/`before` cursor is malformed, was not issued by this\nlisting, or both were sent together. Error code:\n`STORAGE_INVALID_CURSOR`. Restart from the first page (omit the\ncursor).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"}}}}}}
```

## Rename or describe an item

> Updates an item's \`name\` and/or \`description\`. Renaming keeps the item's\
> \`\_id\` stable, so references from flows, exports, imports, and tokens\
> survive; a name already held by another live item in the same folder is\
> rejected with 409 \`STORAGE\_ITEM\_ALREADY\_EXISTS\`. To move an item to a\
> different folder, use \`PATCH /v1/storage/items/{\_id}/move\`; to replace a\
> file's content, use \`PATCH /v1/storage/items/{\_id}/replace\`.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"UpdateRequest":{"type":"object","description":"Request body for renaming an item or changing its description. Provide\n`name`, `description`, or both. Folder placement is changed with the move\nendpoint, not here — `_parentId` and `__ancestorIds` are rejected.","properties":{"name":{"type":"string","maxLength":256,"description":"New display name for the item. Must not match another live item in\nthe same folder (case-sensitive); a collision is rejected with 409\n`STORAGE_ITEM_ALREADY_EXISTS`."},"description":{"type":"string","maxLength":1000,"description":"New description. Pass an empty string to clear it."}}},"StorageItem":{"type":"object","description":"A file or folder stored in Celigo Storage.","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the item. Pass it to download, move, copy,\nreplace, rename, or delete the item; folders and files share one\nID space."},"type":{"type":"string","enum":["file","folder"],"readOnly":true,"description":"Distinguishes a content-bearing file from an organizational folder.\nFixed when the item is created and never changes."},"name":{"type":"string","maxLength":256,"description":"Display name, including the extension for files (for example\n`orders.csv`). Unique among the live items of one folder: files and\nfolders share the namespace, matching is case-sensitive, and a\npending upload holds its name. Any write that would duplicate a name\nis rejected with 409 `STORAGE_ITEM_ALREADY_EXISTS`; items in the\nrecycle bin release theirs."},"_userId":{"type":"string","format":"objectId","readOnly":true,"description":"Environment user that owns the item; every item an account lists carries its own environment's value."},"_parentId":{"type":["string","null"],"format":"objectId","readOnly":true,"description":"Folder that contains this item. Null when the item sits at the\naccount root."},"_integrationId":{"type":["string","null"],"format":"objectId","readOnly":true,"description":"Integration whose system folder tree contains this item. Set on an\nintegration's root folder and inherited by everything created inside\nit; null for user-created folders and files at the account root and\ntheir descendants. Filter the list endpoint with `_integrationId` to\nscope a listing to one integration. The root folder is created with\nthe integration and follows it: deleting the integration sends the\nfolder and everything in it to the recycle bin, and purging the\nintegration purges them."},"integrationName":{"type":"string","readOnly":true,"description":"Name of the integration an integration root folder belongs to. Present\nonly on that root folder (the `isSystem` folder at the account root);\nitems inside the tree carry `_integrationId` but not this field."},"__ancestorIds":{"type":"array","readOnly":true,"description":"Folder IDs from the root down to the immediate parent, in order.\nEmpty for root-level items. Rewritten automatically when the item\nor any ancestor folder is moved.","items":{"type":"string","format":"objectId"}},"size":{"type":"integer","format":"int64","readOnly":true,"description":"File size in bytes; always `0` for folders, which hold no content.\nWhile an upload is pending this reflects the reserved quota, and is\nreconciled to the actual byte count once the upload finalizes."},"mimeType":{"type":"string","maxLength":256,"readOnly":true,"description":"Media type recorded at upload time, used to set the download\nContent-Type. Present when type is file; absent for folders and for\nfiles uploaded without a declared type. Matched case-sensitively by\nthe `mimeType` and `mimeType_nin` list filters."},"description":{"type":"string","maxLength":1000,"description":"Optional caller-supplied note describing the item."},"status":{"type":"string","enum":["pending","active"],"readOnly":true,"description":"Upload lifecycle state. A file is created pending at initiate-upload\nand flips to active once S3 confirms the bytes; folders are created\nactive."},"isSystem":{"type":"boolean","readOnly":true,"description":"When true, the platform created the item (for example a system or\nintegration folder) and protects it — rename, move, content replace,\ncopy, delete, and purge are rejected with 403\n`STORAGE_SYSTEM_ITEM_PROTECTED` (\"System items cannot be modified or\ndeleted\")."},"deletedAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the item was soft-deleted to the recycle bin. Present\nonly for recycle-bin items; the item is permanently purged 30 days\nafter this time unless restored."},"deletedBy":{"type":"string","readOnly":true,"description":"Who soft-deleted the item — a user ID for a direct delete, or\n`cascadeDelete` for a descendant removed when its parent folder was\ndeleted. Present only for recycle-bin items. Cascade-deleted items\ncan be recovered only by restoring the folder that was deleted\ndirectly."},"location":{"type":"object","readOnly":true,"description":"Folder that contains this search hit. Present only in search-mode\nresults from `GET /v1/storage/items` (when the `search` query\nparameter is set).","properties":{"_id":{"type":"string","format":"objectId","description":"ID of the containing folder."},"name":{"type":"string","description":"Name of the containing folder."}}},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the item was created."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of the most recent metadata change. Updated on rename,\nmove, and content replace, and when the folder's direct children\nchange."}},"required":["_id","_userId","type","name","status","isSystem","createdAt","lastModified"]},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/storage/items/{_id}":{"put":{"summary":"Rename or describe an item","description":"Updates an item's `name` and/or `description`. Renaming keeps the item's\n`_id` stable, so references from flows, exports, imports, and tokens\nsurvive; a name already held by another live item in the same folder is\nrejected with 409 `STORAGE_ITEM_ALREADY_EXISTS`. To move an item to a\ndifferent folder, use `PATCH /v1/storage/items/{_id}/move`; to replace a\nfile's content, use `PATCH /v1/storage/items/{_id}/replace`.","operationId":"updateStorageItem","tags":["File Storage"],"parameters":[{"name":"_id","in":"path","required":true,"description":"Item ID.","schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRequest"}}}},"responses":{"200":{"description":"Updated item.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorageItem"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"},"409":{"description":"Another live item in the same folder already has the requested\n`name` (`STORAGE_ITEM_ALREADY_EXISTS`). Nothing was changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Delete an item

> Soft-deletes a file or folder to the recycle bin, where it is retained\
> for 30 days and can be restored with\
> \`POST /v1/storage/items/{\_id}/restore\`. Deleting a folder also removes\
> its entire subtree; the folder is removed immediately and its descendants\
> follow asynchronously. Delete is not blocked by references — flows,\
> exports, imports, and tokens that point at the item are left dangling and\
> fail at runtime — with one exception: a folder that is, or contains, the\
> home folder of a File Server account answers 403\
> \`STORAGE\_SFTP\_HOME\_FOLDER\_PROTECTED\` until that account is deleted or moved\
> to another folder (\`/v1/mft/fileserver/accounts\`). Renaming or moving such\
> a folder is still allowed. A pending multipart upload cannot be deleted\
> here — cancel it with \`POST /v1/storage/files/{\_id}/cancel\`. A pending\
> single PUT upload whose presigned URL is still live is refused with 403\
> \`STORAGE\_PRESIGNED\_URL\_ACTIVE\`; retry after the URL expires.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}}},"paths":{"/v1/storage/items/{_id}":{"delete":{"summary":"Delete an item","description":"Soft-deletes a file or folder to the recycle bin, where it is retained\nfor 30 days and can be restored with\n`POST /v1/storage/items/{_id}/restore`. Deleting a folder also removes\nits entire subtree; the folder is removed immediately and its descendants\nfollow asynchronously. Delete is not blocked by references — flows,\nexports, imports, and tokens that point at the item are left dangling and\nfail at runtime — with one exception: a folder that is, or contains, the\nhome folder of a File Server account answers 403\n`STORAGE_SFTP_HOME_FOLDER_PROTECTED` until that account is deleted or moved\nto another folder (`/v1/mft/fileserver/accounts`). Renaming or moving such\na folder is still allowed. A pending multipart upload cannot be deleted\nhere — cancel it with `POST /v1/storage/files/{_id}/cancel`. A pending\nsingle PUT upload whose presigned URL is still live is refused with 403\n`STORAGE_PRESIGNED_URL_ACTIVE`; retry after the URL expires.","operationId":"deleteStorageItem","tags":["File Storage"],"parameters":[{"name":"_id","in":"path","required":true,"description":"Item ID.","schema":{"type":"string","format":"objectId"}}],"responses":{"204":{"description":"Item soft-deleted. For a folder, the root is removed and its\ndescendants are removed asynchronously."},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"description":"The caller lacks permission for this item; the folder is (or\ncontains) a File Server account's home folder\n(`STORAGE_SFTP_HOME_FOLDER_PROTECTED` — delete or move the account\nfirst); or the item is a pending single PUT upload whose presigned\nURL has not expired yet (`STORAGE_PRESIGNED_URL_ACTIVE` — the upload\nmay still be in flight, so retry once the URL's lifetime has passed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Replace file content

> Overwrites an existing file's content in place. Returns presigned upload\
> URLs in the same shape as one initiate-upload file entry; transfer the new\
> bytes to them just as for a new upload. The item's \`\_id\`, name, and folder\
> placement are preserved, so every reference to the file stays intact — use\
> this instead of delete-and-recreate when a file is referenced by flows or\
> exports. The previous content remains downloadable until the replacement\
> finalizes. Rename with \`PUT /v1/storage/items/{\_id}\` instead; this\
> endpoint does not accept a name.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"ReplaceRequest":{"type":"object","description":"Requests presigned URLs to overwrite an existing file's content in place.\nSame shape as one initiate-upload file entry, minus `name` — the existing\nname, `_id`, and folder placement are preserved. Rename with\n`PUT /v1/storage/items/{_id}` instead.","required":["uploadType"],"properties":{"mimeType":{"type":"string","maxLength":256,"description":"New media type for the replaced content. Optional; the existing\nmimeType is kept when omitted."},"size":{"type":"integer","format":"int64","minimum":0,"description":"Declared size in bytes of the replacement content, used to reserve\nquota. Reconciled to the real size when the upload finalizes."},"uploadType":{"type":"string","enum":["single","multipart"],"description":"How the replacement content will be transferred."},"numParts":{"type":"integer","minimum":1,"description":"Number of parts the replacement will be split into. Required when uploadType is multipart."},"uploadId":{"type":"string","description":"S3 multipart upload ID from a prior replace response. Provide with\n`issuedPartsCount` to resume and request the next batch of part URLs."},"issuedPartsCount":{"type":"integer","minimum":0,"description":"Number of part URLs already issued. Provide together with `uploadId`\nto resume an in-progress replacement."}}},"UploadResult":{"type":"object","description":"Outcome for one file in an upload response. On success, `statusCode` is\n200 and `body` carries the presigned-URL instructions; on failure,\n`statusCode` is a 4xx and `errors` explains why. Each file in a batch is\nvalidated independently, so a single response can mix successes and\nfailures.","required":["name","statusCode"],"properties":{"_id":{"type":"string","format":"objectId","description":"ID of the pending item this result refers to. Present once the item\nis created; absent when the file failed validation before creation.\nUse it to continue a multipart upload, cancel it, or download the\nfile once active."},"name":{"type":"string","description":"File name echoed back from the request, to correlate results."},"statusCode":{"type":"integer","description":"Per-file result status, mirroring HTTP status semantics."},"body":{"$ref":"#/components/schemas/UploadBody"},"errors":{"type":"array","description":"Reasons the file was rejected. Present when statusCode is a 4xx.","items":{"$ref":"#/components/schemas/UploadError"}}}},"UploadBody":{"type":"object","description":"Presigned-URL instructions for one file. The fields present depend on\n`uploadType`: a single PUT returns one `uploadUrl`; a multipart upload\nreturns an `uploadId`, a batch of `partUrls`, and a `completeUrl`.","required":["uploadType"],"properties":{"uploadType":{"type":"string","enum":["single","multipart"],"description":"Transfer mechanism the caller must use for this file."},"uploadUrl":{"type":"string","format":"uri","description":"Presigned S3 URL to PUT the file's bytes to in a single request.\nPresent when uploadType is single. The PUT must carry the header\n`x-amz-server-side-encryption: AES256` — it is part of the signature,\nand S3 rejects the upload with 403 `SignatureDoesNotMatch` without\nit. The URL's `X-Amz-Expires` parameter carries its lifetime and\nthere is no endpoint to reissue it: an expired single PUT means\nstarting a new upload. While the URL is live the pending item holds\nits name and cannot be deleted (`STORAGE_PRESIGNED_URL_ACTIVE`)."},"uploadId":{"type":"string","description":"S3 multipart upload identifier. Present when uploadType is\nmultipart. Send it back to `POST /v1/storage/files/initiateUpload`\nwith the item's `_id` and `issuedPartsCount` to fetch the next batch\nof part URLs once every part already issued has been uploaded."},"partUrls":{"type":"array","description":"Presigned URLs for the next batch of parts. Present when uploadType\nis multipart. A request may return fewer parts than requested when\nthe account-wide part budget is constrained; after uploading every\npart in this batch, fetch the remaining parts by posting to\ninitiateUpload again with the item's `_id`, `uploadId`, and\n`issuedPartsCount` — before then the entry fails with\n`STORAGE_PARTS_MISMATCH`.","items":{"$ref":"#/components/schemas/PartUrl"}},"completeUrl":{"type":"string","format":"uri","description":"Presigned URL to POST the ordered list of part numbers and ETags to,\nfinalizing the multipart upload. Present when uploadType is multipart."}}},"PartUrl":{"type":"object","description":"A presigned URL for uploading one part of a multipart upload.","required":["partNumber","url"],"properties":{"partNumber":{"type":"integer","minimum":1,"description":"1-based index of this part. Send it back as the S3 `partNumber`\nwhen uploading, and keep each part's returned `ETag` to finalize\nthe upload."},"url":{"type":"string","format":"uri","description":"Presigned S3 URL to PUT this part's bytes to."}}},"UploadError":{"type":"object","description":"A per-file failure within a batch upload response.","required":["code","message"],"properties":{"code":{"type":"string","description":"Machine-readable storage error code."},"message":{"type":"string","description":"Human-readable explanation of the failure."}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/storage/items/{_id}/replace":{"patch":{"summary":"Replace file content","description":"Overwrites an existing file's content in place. Returns presigned upload\nURLs in the same shape as one initiate-upload file entry; transfer the new\nbytes to them just as for a new upload. The item's `_id`, name, and folder\nplacement are preserved, so every reference to the file stays intact — use\nthis instead of delete-and-recreate when a file is referenced by flows or\nexports. The previous content remains downloadable until the replacement\nfinalizes. Rename with `PUT /v1/storage/items/{_id}` instead; this\nendpoint does not accept a name.","operationId":"replaceStorageFileContent","tags":["File Storage"],"parameters":[{"name":"_id","in":"path","required":true,"description":"ID of the file to replace.","schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplaceRequest"}}}},"responses":{"200":{"description":"Upload instructions for the replacement content.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResult"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"},"409":{"description":"A concurrent replace is already in progress for this file\n(`STORAGE_ITEM_REPLACE_CONFLICT`). Retry once the in-flight\nreplacement finalizes or is abandoned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Merge a folder into another

> Merges the contents of a source folder into an existing destination\
> folder, then removes the source. Runs asynchronously: the request is\
> validated and queued, and the endpoint immediately returns \`202\` with a\
> job receipt — contents move in the background, and the source folder\
> disappears once the merge completes, without passing through the\
> recycle bin. A file whose name is free in the destination moves with its\
> \`\_id\`. A file whose name the destination already holds overwrites that\
> file: the destination file keeps its \`\_id\` and takes the source file's\
> content, its previous content is discarded, and the source file's \`\_id\`\
> is removed — neither goes to the recycle bin, so references to the\
> source file break. A soft-deleted source folder may be\
> merged, which restores its contents into the destination. Folders only —\
> move individual files with the move endpoint. Requires delete access on\
> the source folder and write access on the destination.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"MergeRequest":{"type":"object","description":"Request body for merging a folder into another folder.","required":["_destinationId"],"properties":{"_destinationId":{"type":"string","format":"objectId","description":"Destination folder that receives the source folder's contents. Must\nreference an existing active folder that is different from the source\nand not one of its descendants."},"trigger":{"type":"string","enum":["move","rename"],"default":"move","description":"How the merge is recorded. Unrecognized values fall back to `move`.\nIgnored when the source folder is soft-deleted — the platform then\nrecords the merge as a restore automatically."}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}}}},"paths":{"/v1/storage/items/{_id}/merge":{"post":{"summary":"Merge a folder into another","description":"Merges the contents of a source folder into an existing destination\nfolder, then removes the source. Runs asynchronously: the request is\nvalidated and queued, and the endpoint immediately returns `202` with a\njob receipt — contents move in the background, and the source folder\ndisappears once the merge completes, without passing through the\nrecycle bin. A file whose name is free in the destination moves with its\n`_id`. A file whose name the destination already holds overwrites that\nfile: the destination file keeps its `_id` and takes the source file's\ncontent, its previous content is discarded, and the source file's `_id`\nis removed — neither goes to the recycle bin, so references to the\nsource file break. A soft-deleted source folder may be\nmerged, which restores its contents into the destination. Folders only —\nmove individual files with the move endpoint. Requires delete access on\nthe source folder and write access on the destination.","operationId":"mergeStorageFolder","tags":["File Storage"],"parameters":[{"name":"_id","in":"path","required":true,"description":"ID of the source folder to merge and remove.","schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MergeRequest"}}}},"responses":{"202":{"description":"Merge accepted and queued. The source folder remains visible until\nthe background job completes, then is removed.","content":{"application/json":{"schema":{"type":"object","required":["jobId","jobType","status","sourceFolderId","_destinationId"],"properties":{"jobId":{"type":"string","description":"Identifier of the queued merge job."},"jobType":{"type":"string","enum":["folderMerge"],"description":"Kind of background job that was queued."},"status":{"type":"string","enum":["accepted"],"description":"Acknowledgement state of the request."},"sourceFolderId":{"type":"string","format":"objectId","description":"The source folder being merged and removed."},"_destinationId":{"type":"string","format":"objectId","description":"The folder receiving the source folder's contents."}}}}}},"400":{"description":"`_destinationId` is missing from the request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"description":"Caller lacks delete access on the source or write access on the\ndestination, the source is a protected system folder, or the account\nis not entitled to Celigo Storage.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The source or destination folder does not exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The merge is structurally invalid — the source or destination is not\na folder, the source and destination are the same folder, the\ndestination sits inside the source (circular merge), or the merged\ntree would exceed the maximum folder depth.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Move an item

> Moves a file or folder to a different folder. Metadata only — the content\
> stays in place, the item's \`\_id\` is unchanged so references survive, and\
> quota is unaffected. Moving a folder re-parents the folder immediately and\
> re-anchors its descendants asynchronously, so the response reflects only\
> the moved item itself. A folder cannot be moved into itself or one of its\
> own descendants. A destination that already holds a live item with the\
> same name rejects the move with 409 \`STORAGE\_ITEM\_ALREADY\_EXISTS\` — send\
> \`name\` to land beside it, or fold a folder into its namesake with\
> \`POST /v1/storage/items/{\_id}/merge\`.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"MoveRequest":{"type":"object","description":"Request body for moving an item to a different folder.","required":["_parentId"],"properties":{"_parentId":{"type":["string","null"],"format":"objectId","description":"Destination folder. Pass null to move the item to the account root.\nThe key is required even when null. Must differ from the current\nparent, must be an active folder, and — for a folder — must not be\nthe item itself or one of its descendants."},"name":{"type":"string","maxLength":256,"description":"New name applied to the item in the same write as the re-parent, so\na name already taken in the destination can be resolved without a\nsecond call. Omit to keep the current name."}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/storage/items/{_id}/move":{"patch":{"summary":"Move an item","description":"Moves a file or folder to a different folder. Metadata only — the content\nstays in place, the item's `_id` is unchanged so references survive, and\nquota is unaffected. Moving a folder re-parents the folder immediately and\nre-anchors its descendants asynchronously, so the response reflects only\nthe moved item itself. A folder cannot be moved into itself or one of its\nown descendants. A destination that already holds a live item with the\nsame name rejects the move with 409 `STORAGE_ITEM_ALREADY_EXISTS` — send\n`name` to land beside it, or fold a folder into its namesake with\n`POST /v1/storage/items/{_id}/merge`.","operationId":"moveStorageItem","tags":["File Storage"],"parameters":[{"name":"_id","in":"path","required":true,"description":"ID of the item to move.","schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveRequest"}}}},"responses":{"200":{"description":"The moved item's changed fields. Only the item's own re-parenting is\nreflected; for a folder, descendants re-anchor asynchronously.","content":{"application/json":{"schema":{"type":"object","required":["_id"],"properties":{"_id":{"type":"string","format":"objectId","description":"ID of the moved item."},"_parentId":{"type":["string","null"],"format":"objectId","description":"New parent folder, or null if moved to the root."},"__ancestorIds":{"type":"array","description":"New ancestor chain from the root to the parent.","items":{"type":"string","format":"objectId"}},"_integrationId":{"type":["string","null"],"format":"objectId","description":"The item's integration after the move — the destination's\nintegration when it landed inside an integration's system\nfolder tree, null when it landed outside one."},"name":{"type":"string","description":"Name of the moved item."}}}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"},"409":{"description":"The destination already holds a live item with the moved item's\nname — or with the `name` sent (`STORAGE_ITEM_ALREADY_EXISTS`).\nNothing was moved.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Copy a file

> Creates an independent copy of a file in the folder named by \`\_parentId\`,\
> with a new \`\_id\` and its own copy of the content. Consumes quota equal to\
> the source file's size. Files only — folder copy is not supported.\
> Without \`name\` the copy is called \`Clone - \` followed by the source\
> file's name; a name already held by a live item in the destination is\
> rejected with 409 \`STORAGE\_ITEM\_ALREADY\_EXISTS\`.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"CopyRequest":{"type":"object","description":"Request body for copying a file.","properties":{"_parentId":{"type":["string","null"],"format":"objectId","description":"Destination folder for the copy. Pass null to copy to the account\nroot; omit it to copy into the source file's own folder. An id that\nnames no folder answers 404 `STORAGE_PARENT_NOT_FOUND`, and a file's\nid 422 `STORAGE_PARENT_NOT_FOLDER`."},"name":{"type":"string","maxLength":256,"description":"Name for the copy. When omitted, the copy is called `Clone - `\nfollowed by the source file's name. Must not match a live item in\nthe destination (case-sensitive): a collision is rejected with 409\n`STORAGE_ITEM_ALREADY_EXISTS`, so a second unnamed copy into the\nsame folder needs an explicit name."}}},"StorageItem":{"type":"object","description":"A file or folder stored in Celigo Storage.","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the item. Pass it to download, move, copy,\nreplace, rename, or delete the item; folders and files share one\nID space."},"type":{"type":"string","enum":["file","folder"],"readOnly":true,"description":"Distinguishes a content-bearing file from an organizational folder.\nFixed when the item is created and never changes."},"name":{"type":"string","maxLength":256,"description":"Display name, including the extension for files (for example\n`orders.csv`). Unique among the live items of one folder: files and\nfolders share the namespace, matching is case-sensitive, and a\npending upload holds its name. Any write that would duplicate a name\nis rejected with 409 `STORAGE_ITEM_ALREADY_EXISTS`; items in the\nrecycle bin release theirs."},"_userId":{"type":"string","format":"objectId","readOnly":true,"description":"Environment user that owns the item; every item an account lists carries its own environment's value."},"_parentId":{"type":["string","null"],"format":"objectId","readOnly":true,"description":"Folder that contains this item. Null when the item sits at the\naccount root."},"_integrationId":{"type":["string","null"],"format":"objectId","readOnly":true,"description":"Integration whose system folder tree contains this item. Set on an\nintegration's root folder and inherited by everything created inside\nit; null for user-created folders and files at the account root and\ntheir descendants. Filter the list endpoint with `_integrationId` to\nscope a listing to one integration. The root folder is created with\nthe integration and follows it: deleting the integration sends the\nfolder and everything in it to the recycle bin, and purging the\nintegration purges them."},"integrationName":{"type":"string","readOnly":true,"description":"Name of the integration an integration root folder belongs to. Present\nonly on that root folder (the `isSystem` folder at the account root);\nitems inside the tree carry `_integrationId` but not this field."},"__ancestorIds":{"type":"array","readOnly":true,"description":"Folder IDs from the root down to the immediate parent, in order.\nEmpty for root-level items. Rewritten automatically when the item\nor any ancestor folder is moved.","items":{"type":"string","format":"objectId"}},"size":{"type":"integer","format":"int64","readOnly":true,"description":"File size in bytes; always `0` for folders, which hold no content.\nWhile an upload is pending this reflects the reserved quota, and is\nreconciled to the actual byte count once the upload finalizes."},"mimeType":{"type":"string","maxLength":256,"readOnly":true,"description":"Media type recorded at upload time, used to set the download\nContent-Type. Present when type is file; absent for folders and for\nfiles uploaded without a declared type. Matched case-sensitively by\nthe `mimeType` and `mimeType_nin` list filters."},"description":{"type":"string","maxLength":1000,"description":"Optional caller-supplied note describing the item."},"status":{"type":"string","enum":["pending","active"],"readOnly":true,"description":"Upload lifecycle state. A file is created pending at initiate-upload\nand flips to active once S3 confirms the bytes; folders are created\nactive."},"isSystem":{"type":"boolean","readOnly":true,"description":"When true, the platform created the item (for example a system or\nintegration folder) and protects it — rename, move, content replace,\ncopy, delete, and purge are rejected with 403\n`STORAGE_SYSTEM_ITEM_PROTECTED` (\"System items cannot be modified or\ndeleted\")."},"deletedAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the item was soft-deleted to the recycle bin. Present\nonly for recycle-bin items; the item is permanently purged 30 days\nafter this time unless restored."},"deletedBy":{"type":"string","readOnly":true,"description":"Who soft-deleted the item — a user ID for a direct delete, or\n`cascadeDelete` for a descendant removed when its parent folder was\ndeleted. Present only for recycle-bin items. Cascade-deleted items\ncan be recovered only by restoring the folder that was deleted\ndirectly."},"location":{"type":"object","readOnly":true,"description":"Folder that contains this search hit. Present only in search-mode\nresults from `GET /v1/storage/items` (when the `search` query\nparameter is set).","properties":{"_id":{"type":"string","format":"objectId","description":"ID of the containing folder."},"name":{"type":"string","description":"Name of the containing folder."}}},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the item was created."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of the most recent metadata change. Updated on rename,\nmove, and content replace, and when the folder's direct children\nchange."}},"required":["_id","_userId","type","name","status","isSystem","createdAt","lastModified"]},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/storage/items/{_id}/copy":{"post":{"summary":"Copy a file","description":"Creates an independent copy of a file in the folder named by `_parentId`,\nwith a new `_id` and its own copy of the content. Consumes quota equal to\nthe source file's size. Files only — folder copy is not supported.\nWithout `name` the copy is called `Clone - ` followed by the source\nfile's name; a name already held by a live item in the destination is\nrejected with 409 `STORAGE_ITEM_ALREADY_EXISTS`.","operationId":"copyStorageFile","tags":["File Storage"],"parameters":[{"name":"_id","in":"path","required":true,"description":"ID of the file to copy.","schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopyRequest"}}}},"responses":{"201":{"description":"The newly created copy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorageItem"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"},"409":{"description":"A live item with the copy's name already exists in the destination\n(`STORAGE_ITEM_ALREADY_EXISTS`). Retry with a different `name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Restore an item

> Restores a soft-deleted item from the recycle bin back to its original\
> location. Restoring a folder also restores the descendants that were\
> removed with it; the folder comes back immediately and its descendants\
> follow asynchronously. Only items deleted directly can be restored — a\
> descendant removed by its parent's deletion comes back only when that\
> parent folder is restored. If a live item has taken the name in the\
> original folder since the delete, the restore is rejected with 409\
> \`STORAGE\_ITEM\_ALREADY\_EXISTS\`; send \`name\` to restore under a new one.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"RestoreRequest":{"type":"object","description":"Optional request body for restoring an item from the recycle bin. Send\nan empty body or omit it to restore under the original name.","properties":{"name":{"type":"string","maxLength":256,"description":"New name applied as the item comes back, so a name taken in its\noriginal folder since the delete can be resolved in the same call.\nOmit to keep the original name."}}},"StorageItem":{"type":"object","description":"A file or folder stored in Celigo Storage.","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the item. Pass it to download, move, copy,\nreplace, rename, or delete the item; folders and files share one\nID space."},"type":{"type":"string","enum":["file","folder"],"readOnly":true,"description":"Distinguishes a content-bearing file from an organizational folder.\nFixed when the item is created and never changes."},"name":{"type":"string","maxLength":256,"description":"Display name, including the extension for files (for example\n`orders.csv`). Unique among the live items of one folder: files and\nfolders share the namespace, matching is case-sensitive, and a\npending upload holds its name. Any write that would duplicate a name\nis rejected with 409 `STORAGE_ITEM_ALREADY_EXISTS`; items in the\nrecycle bin release theirs."},"_userId":{"type":"string","format":"objectId","readOnly":true,"description":"Environment user that owns the item; every item an account lists carries its own environment's value."},"_parentId":{"type":["string","null"],"format":"objectId","readOnly":true,"description":"Folder that contains this item. Null when the item sits at the\naccount root."},"_integrationId":{"type":["string","null"],"format":"objectId","readOnly":true,"description":"Integration whose system folder tree contains this item. Set on an\nintegration's root folder and inherited by everything created inside\nit; null for user-created folders and files at the account root and\ntheir descendants. Filter the list endpoint with `_integrationId` to\nscope a listing to one integration. The root folder is created with\nthe integration and follows it: deleting the integration sends the\nfolder and everything in it to the recycle bin, and purging the\nintegration purges them."},"integrationName":{"type":"string","readOnly":true,"description":"Name of the integration an integration root folder belongs to. Present\nonly on that root folder (the `isSystem` folder at the account root);\nitems inside the tree carry `_integrationId` but not this field."},"__ancestorIds":{"type":"array","readOnly":true,"description":"Folder IDs from the root down to the immediate parent, in order.\nEmpty for root-level items. Rewritten automatically when the item\nor any ancestor folder is moved.","items":{"type":"string","format":"objectId"}},"size":{"type":"integer","format":"int64","readOnly":true,"description":"File size in bytes; always `0` for folders, which hold no content.\nWhile an upload is pending this reflects the reserved quota, and is\nreconciled to the actual byte count once the upload finalizes."},"mimeType":{"type":"string","maxLength":256,"readOnly":true,"description":"Media type recorded at upload time, used to set the download\nContent-Type. Present when type is file; absent for folders and for\nfiles uploaded without a declared type. Matched case-sensitively by\nthe `mimeType` and `mimeType_nin` list filters."},"description":{"type":"string","maxLength":1000,"description":"Optional caller-supplied note describing the item."},"status":{"type":"string","enum":["pending","active"],"readOnly":true,"description":"Upload lifecycle state. A file is created pending at initiate-upload\nand flips to active once S3 confirms the bytes; folders are created\nactive."},"isSystem":{"type":"boolean","readOnly":true,"description":"When true, the platform created the item (for example a system or\nintegration folder) and protects it — rename, move, content replace,\ncopy, delete, and purge are rejected with 403\n`STORAGE_SYSTEM_ITEM_PROTECTED` (\"System items cannot be modified or\ndeleted\")."},"deletedAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the item was soft-deleted to the recycle bin. Present\nonly for recycle-bin items; the item is permanently purged 30 days\nafter this time unless restored."},"deletedBy":{"type":"string","readOnly":true,"description":"Who soft-deleted the item — a user ID for a direct delete, or\n`cascadeDelete` for a descendant removed when its parent folder was\ndeleted. Present only for recycle-bin items. Cascade-deleted items\ncan be recovered only by restoring the folder that was deleted\ndirectly."},"location":{"type":"object","readOnly":true,"description":"Folder that contains this search hit. Present only in search-mode\nresults from `GET /v1/storage/items` (when the `search` query\nparameter is set).","properties":{"_id":{"type":"string","format":"objectId","description":"ID of the containing folder."},"name":{"type":"string","description":"Name of the containing folder."}}},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the item was created."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of the most recent metadata change. Updated on rename,\nmove, and content replace, and when the folder's direct children\nchange."}},"required":["_id","_userId","type","name","status","isSystem","createdAt","lastModified"]},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/storage/items/{_id}/restore":{"post":{"summary":"Restore an item","description":"Restores a soft-deleted item from the recycle bin back to its original\nlocation. Restoring a folder also restores the descendants that were\nremoved with it; the folder comes back immediately and its descendants\nfollow asynchronously. Only items deleted directly can be restored — a\ndescendant removed by its parent's deletion comes back only when that\nparent folder is restored. If a live item has taken the name in the\noriginal folder since the delete, the restore is rejected with 409\n`STORAGE_ITEM_ALREADY_EXISTS`; send `name` to restore under a new one.","operationId":"restoreStorageItem","tags":["File Storage"],"parameters":[{"name":"_id","in":"path","required":true,"description":"ID of the recycle-bin item to restore.","schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestoreRequest"}}}},"responses":{"200":{"description":"The restored item. For a folder, the root is restored and its\ndescendants follow asynchronously.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorageItem"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"},"409":{"description":"A live item in the original folder already has the item's name — or\nthe `name` sent (`STORAGE_ITEM_ALREADY_EXISTS`). The item stays in\nthe recycle bin.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Purge an item

> Permanently deletes a soft-deleted item from the recycle bin and frees its\
> storage. This cannot be undone. Purging a folder also purges its\
> descendants; the folder and its subtree are removed asynchronously. Only\
> items deleted directly can be purged — a descendant removed by its\
> parent's deletion is purged together with that parent. The item must\
> already be in the recycle bin.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}}},"paths":{"/v1/storage/items/{_id}/purge":{"delete":{"summary":"Purge an item","description":"Permanently deletes a soft-deleted item from the recycle bin and frees its\nstorage. This cannot be undone. Purging a folder also purges its\ndescendants; the folder and its subtree are removed asynchronously. Only\nitems deleted directly can be purged — a descendant removed by its\nparent's deletion is purged together with that parent. The item must\nalready be in the recycle bin.","operationId":"purgeStorageItem","tags":["File Storage"],"parameters":[{"name":"_id","in":"path","required":true,"description":"ID of the recycle-bin item to purge.","schema":{"type":"string","format":"objectId"}}],"responses":{"204":{"description":"Item permanently deleted. For a folder, the subtree is purged\nasynchronously."},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Get audit log for a file

> Returns the audit entries recorded for a file, newest first, or 204 with\
> no body when it has none. File operations are no longer written to this\
> audit log, so only entries recorded before that change are returned, and\
> a file created since has none. Each entry records who made the change,\
> when, and which fields were affected. Entries survive purging. Folders\
> have their own route, \`GET /v1/folders/{\_id}/audit\`.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"parameters":{"Limit":{"name":"limit","in":"query","description":"Maximum number of audit entries to return per page.","required":false,"schema":{"type":"integer","minimum":1,"maximum":1000,"default":1000}},"After":{"name":"after","in":"query","required":false,"description":"Opaque pagination cursor for the next page. Take it from the `after`\nvalue in the `Link` response header's `rel=\"next\"` URL rather than\nconstructing it.","schema":{"type":"string"}},"From":{"name":"from","in":"query","required":false,"description":"Only return entries at or after this timestamp. Must be within the last\nthree years and not in the future.","schema":{"type":"string","format":"date-time"}},"To":{"name":"to","in":"query","required":false,"description":"Only return entries at or before this timestamp. Must be within the last\nthree years and not in the future.","schema":{"type":"string","format":"date-time"}},"Action":{"name":"action","in":"query","required":false,"description":"Filter by the change type. Maps to the `event` field on each entry.","schema":{"type":"string","enum":["create","update","delete","download","restore","purge"]}},"Source":{"name":"source","in":"query","required":false,"description":"Filter by how the change was initiated.","schema":{"type":"string","enum":["api","ui","system"]}},"ByUserId":{"name":"_byUserId","in":"query","required":false,"description":"Filter to changes performed by a single user.","schema":{"type":"string","format":"objectId"}}},"schemas":{"StorageAuditEntry":{"type":"object","description":"One change recorded against a file or folder. Returned newest first by\n`GET /v1/files/{_id}/audit` and `GET /v1/folders/{_id}/audit`.","required":["_id","resourceType","_resourceId","event","source","time"],"properties":{"_id":{"type":"string","format":"objectId","description":"Unique identifier for this audit entry."},"resourceType":{"type":"string","enum":["file","folder"],"description":"Type of storage item the entry belongs to. Matches the route used —\n`file` for `/v1/files/{_id}/audit`, `folder` for\n`/v1/folders/{_id}/audit`."},"_resourceId":{"type":"string","format":"objectId","description":"ID of the item this entry belongs to."},"event":{"type":"string","enum":["create","update","delete","download","restore","purge"],"description":"Type of change. An item's history starts with `create`, gathers\n`update` and `download` entries while it is live, and ends with\n`delete` followed by either `restore` (back to live) or `purge`."},"source":{"type":"string","enum":["api","ui","system"],"description":"How the change was initiated."},"time":{"type":"string","format":"date-time","description":"Timestamp when the change occurred."},"byUser":{"type":"object","description":"User who made the change.","properties":{"_id":{"type":"string","format":"objectId","description":"User ID."},"email":{"type":"string","format":"email","description":"User email address."},"name":{"type":"string","description":"User display name."}}},"fieldChanges":{"type":"array","description":"Fields that were modified. Empty on `create` and `delete` entries.","items":{"type":"object","properties":{"fieldPath":{"type":"string","description":"Dot-notation path of the changed field."},"oldValue":{"description":"Previous value (absent on create)."},"newValue":{"description":"New value (absent on delete)."}}}},"deletedInfo":{"type":"object","description":"Metadata about the deleted item. Present on `delete` events.","properties":{"name":{"type":"string","description":"Name of the item at the time of deletion."}}}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}}}},"paths":{"/v1/files/{_id}/audit":{"get":{"summary":"Get audit log for a file","description":"Returns the audit entries recorded for a file, newest first, or 204 with\nno body when it has none. File operations are no longer written to this\naudit log, so only entries recorded before that change are returned, and\na file created since has none. Each entry records who made the change,\nwhen, and which fields were affected. Entries survive purging. Folders\nhave their own route, `GET /v1/folders/{_id}/audit`.","tags":["File Storage"],"operationId":"getStorageFileAudit","parameters":[{"name":"_id","in":"path","description":"File ID.","required":true,"schema":{"type":"string","format":"objectId"}},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/After"},{"$ref":"#/components/parameters/From"},{"$ref":"#/components/parameters/To"},{"$ref":"#/components/parameters/Action"},{"$ref":"#/components/parameters/Source"},{"$ref":"#/components/parameters/ByUserId"}],"responses":{"200":{"description":"Array of audit entries, newest first.","headers":{"Link":{"description":"RFC 5988 pagination link. Includes `<...>; rel=\"next\"` with an\n`after` cursor when more entries exist; absent on the final page.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StorageAuditEntry"}}}}},"204":{"description":"No audit entries exist for this file ID."},"400":{"description":"The `_id` is not a valid ObjectId (`invalid_ref`), or `limit` is out of range (`invalid_query_param`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"422":{"description":"A filter value is invalid — an unknown `action` or `source`\n(`invalid_audit_field`), a `from`/`to` that is not ISO 8601\n(`invalid_timestamp_format`), or a window outside the last three\nyears or in the future (`invalid_time_period`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Get audit log for a folder

> Returns the audit entries recorded for a folder, newest first, or 204\
> with no body when it has none. Folder operations are no longer written\
> to this audit log, so only entries recorded before that change are\
> returned, and a folder created since has none. Each entry records who\
> made the change, when, and which fields were affected. Entries survive\
> purging. Files have their own route, \`GET /v1/files/{\_id}/audit\`.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"parameters":{"Limit":{"name":"limit","in":"query","description":"Maximum number of audit entries to return per page.","required":false,"schema":{"type":"integer","minimum":1,"maximum":1000,"default":1000}},"After":{"name":"after","in":"query","required":false,"description":"Opaque pagination cursor for the next page. Take it from the `after`\nvalue in the `Link` response header's `rel=\"next\"` URL rather than\nconstructing it.","schema":{"type":"string"}},"From":{"name":"from","in":"query","required":false,"description":"Only return entries at or after this timestamp. Must be within the last\nthree years and not in the future.","schema":{"type":"string","format":"date-time"}},"To":{"name":"to","in":"query","required":false,"description":"Only return entries at or before this timestamp. Must be within the last\nthree years and not in the future.","schema":{"type":"string","format":"date-time"}},"Action":{"name":"action","in":"query","required":false,"description":"Filter by the change type. Maps to the `event` field on each entry.","schema":{"type":"string","enum":["create","update","delete","download","restore","purge"]}},"Source":{"name":"source","in":"query","required":false,"description":"Filter by how the change was initiated.","schema":{"type":"string","enum":["api","ui","system"]}},"ByUserId":{"name":"_byUserId","in":"query","required":false,"description":"Filter to changes performed by a single user.","schema":{"type":"string","format":"objectId"}}},"schemas":{"StorageAuditEntry":{"type":"object","description":"One change recorded against a file or folder. Returned newest first by\n`GET /v1/files/{_id}/audit` and `GET /v1/folders/{_id}/audit`.","required":["_id","resourceType","_resourceId","event","source","time"],"properties":{"_id":{"type":"string","format":"objectId","description":"Unique identifier for this audit entry."},"resourceType":{"type":"string","enum":["file","folder"],"description":"Type of storage item the entry belongs to. Matches the route used —\n`file` for `/v1/files/{_id}/audit`, `folder` for\n`/v1/folders/{_id}/audit`."},"_resourceId":{"type":"string","format":"objectId","description":"ID of the item this entry belongs to."},"event":{"type":"string","enum":["create","update","delete","download","restore","purge"],"description":"Type of change. An item's history starts with `create`, gathers\n`update` and `download` entries while it is live, and ends with\n`delete` followed by either `restore` (back to live) or `purge`."},"source":{"type":"string","enum":["api","ui","system"],"description":"How the change was initiated."},"time":{"type":"string","format":"date-time","description":"Timestamp when the change occurred."},"byUser":{"type":"object","description":"User who made the change.","properties":{"_id":{"type":"string","format":"objectId","description":"User ID."},"email":{"type":"string","format":"email","description":"User email address."},"name":{"type":"string","description":"User display name."}}},"fieldChanges":{"type":"array","description":"Fields that were modified. Empty on `create` and `delete` entries.","items":{"type":"object","properties":{"fieldPath":{"type":"string","description":"Dot-notation path of the changed field."},"oldValue":{"description":"Previous value (absent on create)."},"newValue":{"description":"New value (absent on delete)."}}}},"deletedInfo":{"type":"object","description":"Metadata about the deleted item. Present on `delete` events.","properties":{"name":{"type":"string","description":"Name of the item at the time of deletion."}}}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}}}},"paths":{"/v1/folders/{_id}/audit":{"get":{"summary":"Get audit log for a folder","description":"Returns the audit entries recorded for a folder, newest first, or 204\nwith no body when it has none. Folder operations are no longer written\nto this audit log, so only entries recorded before that change are\nreturned, and a folder created since has none. Each entry records who\nmade the change, when, and which fields were affected. Entries survive\npurging. Files have their own route, `GET /v1/files/{_id}/audit`.","tags":["File Storage"],"operationId":"getStorageFolderAudit","parameters":[{"name":"_id","in":"path","description":"Folder ID.","required":true,"schema":{"type":"string","format":"objectId"}},{"$ref":"#/components/parameters/Limit"},{"$ref":"#/components/parameters/After"},{"$ref":"#/components/parameters/From"},{"$ref":"#/components/parameters/To"},{"$ref":"#/components/parameters/Action"},{"$ref":"#/components/parameters/Source"},{"$ref":"#/components/parameters/ByUserId"}],"responses":{"200":{"description":"Array of audit entries, newest first.","headers":{"Link":{"description":"RFC 5988 pagination link. Includes `<...>; rel=\"next\"` with an\n`after` cursor when more entries exist; absent on the final page.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StorageAuditEntry"}}}}},"204":{"description":"No audit entries exist for this folder ID."},"400":{"description":"The `_id` is not a valid ObjectId (`invalid_ref`), or `limit` is out of range (`invalid_query_param`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"422":{"description":"A filter value is invalid — an unknown `action` or `source`\n(`invalid_audit_field`), a `from`/`to` that is not ISO 8601\n(`invalid_timestamp_format`), or a window outside the last three\nyears or in the future (`invalid_time_period`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Get storage usage

> Returns account-wide storage usage and entitlement, aggregated across all\
> environments: bytes in use (active and recycle bin), the licensed quota,\
> the hard limit at which uploads are blocked, and whether the account is in\
> overage.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"Usage":{"type":"object","description":"Account-wide storage usage and entitlement, aggregated across all environments.","required":["activeUsage","recycleBinUsage","totalUsage","maxAllowedUsage","isOverage","hardLimit","overage"],"properties":{"activeUsage":{"type":"integer","format":"int64","description":"Bytes consumed by active and pending files."},"recycleBinUsage":{"type":"integer","format":"int64","description":"Bytes consumed by soft-deleted files still retained in the recycle bin."},"totalUsage":{"type":"integer","format":"int64","description":"Sum of activeUsage and recycleBinUsage. This is the figure checked\nagainst hardLimit when an upload is attempted."},"systemUsage":{"type":"integer","format":"int64","description":"Bytes consumed by system items (folders and files the platform\ncreated). Present only when the request sets `includeSystem=true`."},"maxAllowedUsage":{"type":"integer","format":"int64","description":"Storage quota in bytes granted by the account's license."},"isOverage":{"type":"boolean","description":"When true, totalUsage has passed maxAllowedUsage and the account is\nconsuming the overage buffer. Uploads still succeed until totalUsage\nreaches hardLimit."},"hardLimit":{"type":"integer","format":"int64","description":"Byte ceiling at which further uploads are rejected with\n`STORAGE_QUOTA_EXCEEDED`. Equals maxAllowedUsage plus the licensed\noverage buffer, or maxAllowedUsage exactly when overage is disabled."},"overage":{"type":"integer","format":"int64","description":"Bytes currently consumed beyond maxAllowedUsage. Zero when not in overage."}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/storage/usage":{"get":{"summary":"Get storage usage","description":"Returns account-wide storage usage and entitlement, aggregated across all\nenvironments: bytes in use (active and recycle bin), the licensed quota,\nthe hard limit at which uploads are blocked, and whether the account is in\noverage.","operationId":"getStorageUsage","tags":["File Storage"],"parameters":[{"name":"includeSystem","in":"query","required":false,"description":"When true, also returns `systemUsage` — the bytes consumed by system items.","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Account-wide usage and entitlement.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Usage"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Get storage usage by environment

> Returns storage usage per environment, with the account-wide total and\
> the quota figures \`GET /v1/storage/usage\` reports. Use it to see which\
> environment is consuming the account's shared quota.

```json
{"openapi":"3.2.0","info":{"title":"File Storage","version":"1.0.0"},"tags":[{"name":"File Storage","description":"Celigo Storage is managed file storage built into integrator.io for staging files between flows and giving integrations persistent files to work with. Organize content into files and folders, transfer bytes directly through short-lived presigned S3 URLs, and reference stored files from flows. Items are addressed by `_id` and placed by parent folder, where names are unique among live items; renames and moves keep the `_id` stable, and deletes go to a 30-day recycle bin before they can be purged.\n\n## Storage item schema\n\n{% openapi-schemas spec=\"filestorage\" schemas=\"StorageItem\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"UsageEnvironments":{"type":"object","description":"Per-environment usage breakdown alongside the account-wide quota\nfigures returned by `GET /v1/storage/usage`. The active / recycle-bin\nsplit is reported per environment; the account-wide total is the sum\nof the rows.","required":["environments","totalUsage","maxAllowedUsage","isOverage","hardLimit","overage"],"properties":{"environments":{"type":"array","description":"One entry per environment in the account.","items":{"$ref":"#/components/schemas/EnvironmentUsage"}},"totalUsage":{"type":"integer","format":"int64","description":"Account-wide bytes consumed by active, pending and soft-deleted files across every environment."},"systemUsage":{"type":"integer","format":"int64","description":"Account-wide bytes consumed by system items. Present only when the\nrequest sets `includeSystem=true`."},"maxAllowedUsage":{"type":"integer","format":"int64","description":"Storage quota in bytes granted by the account's license."},"isOverage":{"type":"boolean","description":"When true, totalUsage has passed maxAllowedUsage and the account is in the overage buffer."},"hardLimit":{"type":"integer","format":"int64","description":"Byte ceiling at which further uploads are rejected."},"overage":{"type":"integer","format":"int64","description":"Bytes currently consumed beyond maxAllowedUsage. Zero when not in overage."}}},"EnvironmentUsage":{"type":"object","description":"Storage usage for a single environment.","required":["environment","activeUsage","recycleBinUsage","totalUsage"],"properties":{"environment":{"type":"object","description":"Environment the usage belongs to.","required":["_id","name"],"properties":{"_id":{"type":"string","format":"objectId","description":"Environment id, as returned by `GET /v1/environments`."},"name":{"type":"string","description":"Environment display name."}}},"activeUsage":{"type":"integer","format":"int64","description":"Bytes consumed by active and pending files in this environment."},"recycleBinUsage":{"type":"integer","format":"int64","description":"Bytes consumed by this environment's soft-deleted files."},"totalUsage":{"type":"integer","format":"int64","description":"Sum of this environment's activeUsage and recycleBinUsage."}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/storage/usage/environments":{"get":{"summary":"Get storage usage by environment","description":"Returns storage usage per environment, with the account-wide total and\nthe quota figures `GET /v1/storage/usage` reports. Use it to see which\nenvironment is consuming the account's shared quota.","operationId":"getStorageUsageByEnvironment","tags":["File Storage"],"parameters":[{"name":"includeSystem","in":"query","required":false,"description":"When true, also returns `systemUsage` in the account-wide totals.","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Per-environment breakdown with account-wide totals.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageEnvironments"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```
