Bucket
Lists, reads, stores, checks and deletes objects in a bucket you own, and signs links to them. Seven operations over anything that speaks S3 — Cloudflare R2, AWS S3, MinIO, Backblaze B2 — and the one most people come for is a temporary download link for a private file, the thing you hand a buyer without the file ever passing through HostWebhook.
Setup
One credential, of type AWS S3 / Compatible: the endpoint, the bucket name, the region, and an access key pair. Add it under Settings → Credentials, then pick it in the node.
The node is called Bucket and not R2 because the provider is an endpoint, not a kind of node. The same node talks to R2, to S3, to a MinIO you run yourself or to Backblaze without a line of difference — what changes is the URL in the credential. R2 signs with region auto; S3 wants the bucket's real region.
Operations
The Operation dropdown decides what the node does and which fields appear under it. These are the exact values the API validates against:
| Value | In the dropdown | S3 call | What it does |
|---|---|---|---|
| presignDownload | Sign download link | GET (presigned) | Returns a temporary URL for a private object. Does not move the file. |
| putObject | Upload object | PUT | Stores an object. Overwrites the key if it already exists. |
| headObject | Check object exists | HEAD | Says whether the object is there, and how big, without downloading it. |
| deleteObject | Delete object | DELETE | Deletes an object. There is no trash. |
| listObjects | List objects | GET ?list-type=2 | Lists the files — and, grouped, the folders — under a prefix. The only iterable operation: the node below runs once per file. |
| getObject | Read object | GET | Puts the contents of an object in the payload, as text, parsed JSON or Base64. Up to 5 MB. |
| presignUpload | Sign upload link | PUT (presigned) | Returns a temporary URL someone else can upload a file to, without the credential. |
In the dropdown they are grouped by what they return: the two Sign operations under Links, the five that touch the bucket under Objects. There is no multipart upload — it needs resumable state and per-part retries — so an upload through the node stops at 25 MB and a read at 5 MB. For anything bigger, sign a link and let the file travel between the person and the bucket without passing through the flow.
Which one you want
Sign download link
Use it when someone outside your system has to read one private object, once, soon. A buyer who just paid; a customer who asked support for their invoice; a partner who needs today's export. The object stays private and the bucket stays closed — what you hand out is a URL that carries its own permission and stops working on a deadline.
It never touches the network — neither of the two Sign operations does: they sign and return. That makes it fast, and it means a link for an object that does not exist is produced just as happily as one for an object that does — the failure shows up when the link is clicked. If that matters, put a Check object exists in front of it.
downloadAs is worth the extra field. Left empty, the browser decides what to do with the file and usually opens the PDF in a tab. Filled in, the browser downloads it under that name. It is the difference between “here is your booklet” and “a PDF has appeared in a tab”, and the name travels inside the signature, so nobody can rewrite it on the way. Accents and other non-English letters are kept: the name is sent the standard way for them, so Safari and command-line tools save it right too.
Sign upload link
The mirror image: someone outside your system has to put a file in your bucket — a customer sending a signed contract, a supplier dropping this week's catalogue — and you don't want to give them the credential. The node returns a URL and method: "PUT"; whoever holds it uploads with a plain HTTP PUT to that one key, until it expires. The file goes straight to the bucket, so the node's 25 MB upload limit doesn't apply.
contentType is what the object will be stored as, and it is not enforced: the bucket keeps whatever arrives. Making it part of the signature would force the uploader to send that exact header or get a 403 they cannot debug.
Upload object
Use it to keep the thing you would otherwise have to reconstruct later: the raw event as it arrived, a receipt, a rendered report, a snapshot of a payload before a transform mangled it. The contents field takes templates, so an object is written as JSON and a string is written as it stands.
It overwrites. There is no “fail if it exists” flag, because S3 does not have one — a PUT to a key that is taken replaces it, quietly and successfully. If a key can collide, make the key unique (an id, a timestamp) rather than hoping.
Check object exists
Use it as a gate. The typical shape is a flow that must not promise something the bucket cannot deliver: check that the file for this product is really there before signing a link and emailing it, or before charging for it.
failIfMissing decides who handles the absence. With Yes a missing object stops the flow right there, which is what you want when there is nothing sensible to do without the file. With No the step succeeds and hands exists: false downstream, so a Filter or Conditional can branch — alert the team, fall back to a default file, carry on.
Read object
Use it when the next step needs the contents, not a link to them: a JSON settings file, a CSV to parse, an email template, the receipt an earlier run stored. Read as decides the shape. Text puts the UTF-8 in content; JSON parses it into content, and a body that does not parse fails the step; Base64 puts the bytes in contentBase64. A PDF or an image needs Base64 — read as text it arrives corrupted — and Base64 weighs a third more than the file.
Reads stop at 5 MB, and past that the step fails, with the real size and a pointer to Sign download link. It never hands down half a file: cut contents still parse, still get emailed, and nobody notices. Over 256 KB the contents flow on whole to the next node, but the run history stores them trimmed. failIfMissing works as in Check object exists above.
List objects
Use it to look inside the bucket: what arrived in uploads/ today, which files a customer has, what is left in tmp/. Prefix narrows it — empty lists the whole bucket. Group into folders (the default) returns the files at that level plus the names of the folders below it, the way a person browses; No returns every key under the prefix, however deep.
It is the only iterable operation: the node below runs once per file, and each run gets one entry — its key, size, lastModified and etag. Folders travel alongside the list and are not walked. A prefix with nothing under it gives count: 0 and the node below runs zero times, with no error — correct for an empty list, and easy to mistake for a flow that did nothing.
Scheduled Workflow every night at 03:00
→ Bucket "What is in tmp" operation: listObjects
prefix: tmp/
groupIntoFolders: No
→ Bucket "Delete it" operation: deleteObject ← runs once per file
key: {{payload.key}}Maximum objects is 1,000 by default and goes up to 5,000; the provider pages at 1,000 and the node follows the pages for you. When there are more, the step still succeeds — truncated: true says so, and nextToken is where the next run carries on: put {{payload.nextToken}}, or the token itself, in Continue after. When the last run stopped short, the node page says so, with a button that fills that field for you.
A fixed prefix lists once, however many items the event carries. A prefix templated from the payload — customers/{{payload.customerId}}/ — lists once per item, and two items that name the same prefix are listed once. The listings then reach the node below merged into one — the objects together, the folders without repeats — with a parts entry per listing giving its prefix, count, truncated and nextToken.
Delete object
Use it for cleanup you own: the temporary render you just emailed, the upload a customer asked you to erase, yesterday's export. It cannot be undone, and it does not tell you whether there was anything to delete — S3 answers the same way either way.
What downstream nodes receive
The node hands its own result to whatever comes next, replacing the payload it was given. After a Sign download link:
{
"_meta": { "iterable": false, "count": 1 },
"statusCode": 200,
"operation": "presignDownload",
"key": "booklets/sustainable-changes-1.pdf",
"url": "https://<account>.r2.cloudflarestorage.com/booklets/…?X-Amz-Signature=…",
"expiresIn": 172800,
"expiresAt": "2026-09-05T12:00:00.000Z",
"downloadAs": "Sustainable Changes 1.pdf"
}The other object operations return the same key plus what they learned. A Sign upload link adds url, method ("PUT"), expiresIn, expiresAt and contentType. An upload adds size, contentType and etag. A check adds exists, and when the object is there also size, contentType, etag and lastModified — an ISO 8601 date, the same format the listing uses. A read adds the same, plus encoding and the contents — in content, or contentBase64 for Base64. A delete adds deleted.
{{payload.url}} for the link. Anything else it needs from the original event it has to fetch by name, with a cross-node reference — {{$("Stripe sale").payload.…}} — in a node that supports them, such as Email, Notification or Code. The Bucket's own fields do not: they see the payload they were handed and nothing else, so chain the Bucket nodes so that each one gets what it needs from the one above.A listing is the exception, and the only iterable payload the node produces. The node below runs once per entry of objects; everything else rides along, once:
{
"_meta": { "iterable": true, "iterateField": "objects", "count": 2 },
"statusCode": 200,
"operation": "listObjects",
"prefix": "booklets/",
"delimiter": "/",
"objects": [
{ "key": "booklets/one.pdf", "size": 482113, "lastModified": "2026-09-01T10:00:00.000Z", "etag": "\"9b2c…\"" },
{ "key": "booklets/two.pdf", "size": 530774, "lastModified": "2026-09-03T08:12:00.000Z", "etag": "\"41fa…\"" }
],
"folders": ["booklets/drafts/"],
"count": 2,
"folderCount": 1,
"truncated": false,
"nextToken": null
}delimiter is "/" when the listing is grouped into folders and null when it is flat. The other six operations act on a single object, so the node below receives a result and not a list — a Loop underneath has nothing to walk.
Example — selling a file
The flow this node was built for. Someone buys a PDF; they get a link that works for two days; the file never leaves your bucket and never passes through HostWebhook.
Webhook "Stripe sale" the payment provider posts checkout.session.completed
→ Filter payload.type equals "checkout.session.completed"
→ Bucket "Check the file" operation: headObject
key: booklets/{{payload.data.object.metadata.sku}}.pdf
failIfMissing: Yes
→ hands down { key, exists, size, contentType, … }
→ Bucket "Sign the link" operation: presignDownload
key: {{payload.key}} ← from the check above
expiresIn: 172800 (48 hours)
downloadAs: Sustainable Changes 1.pdf
→ hands down { key, url, expiresAt, … }
→ Email Action to: {{$("Stripe sale").payload.data.object.customer_details.email}}
html: <a href="{{payload.url}}">Download your booklet</a>
This link expires on {{payload.expiresAt}}.Three details carry the whole thing. The key is written once, in the check, and the signing node picks it up as {{payload.key}} — the check hands its own key down, so the SKU is not read out of the sale twice and the two nodes cannot drift apart. The email's recipient comes from the webhook node by name, because by that point the payload is the signed link and no longer the sale. And the deadline goes in the email: a buyer who opens it on Monday should be told, not left to find out.
The check in front is optional and cheap. Without it a missing file still produces a perfectly valid link, the email still goes out, and the buyer is the one who discovers the problem.
Two things about signed links
AI toolkit mode
Once the credential is picked, the node asks for a Mode. Single operation is everything above: one operation, your templates, every run. AI toolkit turns the node into seven tools that an AI Node — or an MCP client, through one of your MCP servers — calls by name, with the key and the options decided per call.
| Tool | Does | Arguments |
|---|---|---|
| list_objects | Lists the files under a prefix, with key, size and date — where the model starts to find a key | prefix? · group_into_folders? · max_keys? · start_after? |
| read_object | Returns the contents of a file (up to 5 MB) as text, JSON or base64 | key · as? · fail_if_missing? |
| get_object_info | Whether a file exists, and its size, type and date, without downloading it | key · fail_if_missing? |
| get_download_link | A temporary link that downloads a private file, to hand to a person | key · expires_in_seconds? · download_as? |
| upload_object | Stores text as a file, exactly as given (an empty text is an empty file). Overwrites the key if it exists, so it counts as destructive | key · content · content_type? |
| create_upload_link | A temporary link someone can PUT a file to, without the credential | key · expires_in_seconds? · content_type? |
| delete_object | Deletes a file. Blocked by default. | key |
upload_object and delete_object are the destructive ones — each can wipe out a file that was already there. An AI Node with Block destructive tools turned on refuses them, and an MCP client runs them with run_destructive_operation.
What you control
Everything below lives in the Settings card of the node's AI Toolkit section — all optional: the defaults work.
- Where the model can work. One folder (the default) keeps the model inside the folder you write — a key or a prefix outside it, or with a
..segment, is refused before the bucket is called, and listings start there. With no folder written it sees the whole bucket. The whole bucket opens it up even if a folder is written. Which bucket is never the model's choice: the credential fixes it. - Characters per call sent to the model. 20,000 by default (about 5,000 tokens), anything from 1,000 to 200,000. A longer file is cut there.
- Deleting starts blocked. A deleted file can't be brought back. Unblock
delete_objectunder Block these tool calls if the agent needs it; block any other tool — uploads, say — the same way.
What goes back to the model
The same response as the operation, with defaults that suit a model: a listing returns up to 200 files, flat, and says how to continue (start_after set to nextToken); links last one hour unless it asks for anything from 60 seconds to 7 days; and a file that isn't there answers exists: false instead of an error, so the model can decide. File contents stop at the characters limit, and every cut is listed in a notes field. Arguments are used as written: a {{payload.x}} the model writes is not resolved, so it can't read the output of other nodes in the flow.
Testing a tool
operation in their payload that names another tool is refused.{ "operation": "list_objects", "prefix": "clients/" }Gotchas
- Keys have no leading slash and no
..segments.booklets/one.pdf, not/booklets/one.pdf— the node rejects both rather than writing to a key you did not mean. A prefix is refused with a leading slash too, but it can be empty (the whole bucket) and end in one. - Upload tops out at 25 MB. What does not fit needs multipart, which this node does not do — or a Sign upload link, which sends the file straight to the bucket.
- Read tops out at 5 MB, and fails rather than cuts. Past it, sign a download link instead. A binary file read as text arrives corrupted: read it as Base64.
- A listing stops at Maximum objects — 1,000 unless you raise it, 5,000 at most. The step still succeeds;
truncated: trueandnextTokensay there is more and where to carry on. Some S3 implementations say “more” without a token — thennextTokenisnulland the way forward is a narrower prefix. - An empty listing runs the node below zero times. No error, no run — check
countif an empty folder should raise an alarm. - An upload overwrites. A PUT to an existing key replaces it and reports success.
- A missing object can answer 403, not 404. S3 and R2 both do it when the credential cannot list the bucket, so the check treats the two the same: not there. It also means a genuinely forbidden key reads as missing. Read object does not: there a 403 stops the step, because it can just as well mean the credential lost read access — and a flow that recreates or deletes “missing” files should not act on that.
deleted: truedoes not mean something was deleted. S3 answers 204 whether the key was there or not — deletion is idempotent by design, and there is no way to tell the two apart without a check first.- Signing does not verify. A link — download or upload — is signed for whatever key you give it, existing or not.
- The credential must be
aws_s3. Any other type is refused when the node is created, with the reason.