Your generation step returns a signed URL that dies in an hour. Drop one node between it and your publish step, and every downstream API gets a public URL that serves raw bytes — for as long as your schedule needs.
One HTTP Request node, configured once. No community node install, no credentials wizard beyond a header.
Place it directly after the node that produces the image — OpenAI (GPT Image), Replicate, Higgsfield, an HTTP call to Seedream or Nano Banana, anything that returns a file URL. Configure it like this:
| Setting | Value |
|---|---|
| Method | POST |
| URL | https://curlyflies.com/v1/upload-url |
| Authentication | Generic Credential Type → Header Auth |
| Header name | Authorization |
| Header value | Bearer curly_live_... |
| Body Content Type | JSON |
// JSON body — map the upstream node's file URL in { "url": "{{ $json.image_url }}", "ttl_seconds": 604800 }
No binary passes through n8n — CurlyFlies downloads the source URL itself while it’s still valid and re-hosts the bytes. The node’s output:
{
"file_id": "x7k2p9",
"url": "https://curlyflies.com/f/x7k2p9.png",
"expires_at": "2026-06-19T09:41:00Z",
"size_bytes": 204800,
"content_type": "image/png",
"delete_token": "dt_abc123"
}
{{ $json.url }} in your publish nodeIn your Instagram (Facebook Graph API) node, Buffer node, or any downstream HTTP call, use {{ $json.url }} from the CurlyFlies node as the image_url / media URL. The crawler fetches it, gets 200 + image/png + raw bytes, and accepts the post. Check expires_at if you persist URLs — set ttl_seconds longer than your furthest scheduled slot.
One node between generation and publishing. Everything upstream and downstream stays exactly as it is.
GPT Image / Nano Banana / Seedream / Replicate→ signed URL, dies in ~1h
HTTP Request nodePOST /v1/upload-url → stable public URL
Instagram Graph API / Buffer / scheduleimage_url = {{ $json.url }}
This is the failure it fixes: generation APIs return temporary signed URLs (Azure blob links with se= expiry, S3 presigned URLs). Fast runs publish before expiry; runs with queues, retries, waits, or approval steps don’t — and Instagram answers with error 9004, “media fetch failed.” Scheduled posts make it worse: the URL must still be alive when the schedule fires, not just when the workflow runs. Re-host immediately after generation, set the TTL past your schedule, and the intermittent failures disappear. Full error-9004 guide →
One HTTP Request node: POST https://curlyflies.com/v1/upload-url with Header Auth (Authorization: Bearer curly_live_...) and a JSON body of {"url": "{{ $json.image_url }}", "ttl_seconds": 604800}. The response’s url field is a clean public URL that serves raw bytes with the correct Content-Type.
Almost always an expired or non-public media URL. Generation APIs return signed URLs that die within roughly an hour, and Drive/Dropbox links serve HTML viewer pages instead of the file. Instagram’s crawler fetches your image_url itself, anonymously — if it can’t get raw bytes, you get 9004. Troubleshooting guide →
Longer than your furthest-out slot, plus margin. A weekly calendar → "ttl_seconds": 604800 (7 days). You can also send an absolute expires_at datetime instead, which matches how schedulers think. Free plan TTL is fixed at 24h; longer TTLs need Builder (30d), Pro (60d), or Scale (90d).
Yes. Use POST https://curlyflies.com/v1/upload with Body Content Type Form-Data and the binary property mapped to the file field. Same auth header, same response shape. upload-url is usually simpler because n8n never has to hold the bytes.
Drive/Dropbox share links fail crawler validation outright (they serve HTML). S3 works once you’ve configured buckets, policies, and Content-Type metadata — about 45 minutes of AWS setup. CurlyFlies is one node and the files clean themselves up after the TTL. See the full comparison →
Same call your HTTP Request node will make — try it from your terminal first.
$ curl -X POST https://curlyflies.com/v1/upload-url \ -H "Authorization: Bearer $CURLY_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://...signed-url...", "ttl_seconds": 604800}' → { "url": "https://curlyflies.com/f/x7k2p9.png", "expires_at": "2026-06-19T09:41:00Z" }