# CurlyFlies — Full Documentation for Agents > Publishing infrastructure for AI-generated content. Upload a file, get a publish-grade public URL in under 200ms — direct bytes, correct Content-Type, no cookies, no redirects, no viewer pages. URLs pass Instagram/Meta/Buffer crawler validation (fixes error 9004). Files live up to 90 days (plan-dependent), then vanish. No bucket setup, no OAuth, no human in the loop. Base URL: https://curlyflies.com/v1 Auth: Authorization: Bearer {api_key} on every request. ===================================================================== ## 1. GET AN API KEY (no human required) POST https://curlyflies.com/v1/agent/signup Content-Type: application/json { "email": "agent@workflow.ai", "agent_name": "my-n8n-workflow" } Response: { "api_key": "curly_live_abc123", "plan": "free", "uploads_remaining": 100, "message": "API key created. Store it securely — it won't be shown again." } Rate limit: 5 signups/hour per IP. ===================================================================== ## 2. UPLOAD A FILE POST https://curlyflies.com/v1/upload Content-Type: multipart/form-data Fields: file binary (required if content_base64 not provided) content_base64 string (required if file not provided) filename string (optional) content_type string (optional, auto-detected from magic bytes) ttl_seconds integer (optional, default 86400, max varies by plan) Example: curl -X POST https://curlyflies.com/v1/upload \ -H "Authorization: Bearer $CURLY_KEY" \ -F "file=@output.png" Response 200: { "file_id": "x7k2p9", "url": "https://curlyflies.com/f/x7k2p9.png", "expires_at": "2026-06-02T09:41:00Z", "size_bytes": 204800, "content_type": "image/png", "delete_token": "dt_abc123" } The url is public: no auth, no redirects, correct Content-Type. It passes Instagram Graph API URL validation. Always read expires_at — the URL stops working after that time. Allowed types: image/jpeg, image/png, image/gif, image/webp, video/mp4, video/quicktime, application/pdf, text/plain, application/json. Executables and scripts are rejected. ===================================================================== ## 3. UPLOAD FROM A REMOTE URL (re-hosting) POST https://curlyflies.com/v1/upload-url Content-Type: application/json { "url": "https://oaidalleapiprodscus.blob.core.windows.net/private/gen/abc123.png?se=...", "filename": "output.png", "ttl_seconds": 86400 } Response: same shape as /v1/upload. Use this when you have an image URL from GPT Image (OpenAI), Nano Banana (Google), Seedream, Flux, Midjourney, Replicate, Higgsfield, or any source with signed/expiring URLs that downstream APIs cannot fetch. ===================================================================== ## 4. GET FILE INFO GET https://curlyflies.com/v1/files/{file_id} Response 200: { "file_id": "x7k2p9", "url": "...", "expires_at": "...", "size_bytes": 204800, "content_type": "image/png", "exists": true } Response 404: { "error": "file_not_found", "message": "File x7k2p9 does not exist or has expired." } ===================================================================== ## 5. DELETE A FILE DELETE https://curlyflies.com/v1/files/{file_id} Headers: X-Delete-Token: {delete_token} (or the owning Bearer key) Response 200: { "deleted": true, "file_id": "x7k2p9" } ===================================================================== ## 6. USAGE STATS GET https://curlyflies.com/v1/usage Response 200: { "plan": "builder", "uploads_this_month": 847, "uploads_limit": 2000, "uploads_remaining": 1153, "reset_date": "2026-07-01T00:00:00Z" } ===================================================================== ## 7. EXPIRY WEBHOOKS (Builder plan and above) PUT https://curlyflies.com/v1/webhook { "url": "https://your-endpoint.example/hook" } (must be https) ~24 hours before any of your files expires, CurlyFlies POSTs: { "event": "file.expiring", "file_id": "x7k2p9", "url": "...", "expires_at": "...", "expires_in_seconds": 86210 } Use it to re-upload or reschedule before a URL goes dead. GET /v1/webhook returns the current registration; DELETE /v1/webhook removes it. ===================================================================== ## 8. MCP SERVER URL: https://mcp.curlyflies.com/mcp Transport: Streamable HTTP Auth: Bearer token in Authorization header Client config: { "mcpServers": { "curlyflies": { "url": "https://mcp.curlyflies.com/mcp", "apiKey": "your_key_here" } } } Tools: - upload_file(content_base64, filename, content_type, ttl_seconds) Upload bytes or base64. Returns { url, expires_at, file_id, size_bytes } - upload_from_url(url, ttl_seconds) Re-host any remote URL. Handles GPT Image, Nano Banana, Seedream, Replicate, Higgsfield. - get_file_info(file_id) Check if a file is still live before passing it downstream. - delete_file(file_id) Manual cleanup. Returns { deleted: true } ===================================================================== ## 9. PLANS & LIMITS Free: $0 100 uploads/mo 5MB max 24h TTL 100 req/hr Builder: $9/mo 2,000 uploads/mo 250MB max up to 30 days 500 req/hr Pro: $29/mo 10,000 uploads/mo 1GB max up to 60 days 2,000 req/hr Scale: $99/mo 50,000 uploads/mo 5GB max up to 90 days 10,000 req/hr Overage: $0.002 per upload above plan limit. ===================================================================== ## 10. ERROR CODES 401 unauthorized Invalid or missing API key 403 forbidden / content_blocked 404 file_not_found File does not exist or has expired 413 file_too_large File exceeds plan size limit 415 unsupported_file_type / content_type_mismatch / invalid_image 422 fetch_failed Remote URL could not be fetched 429 rate_limit_exceeded / upload_quota_exceeded (Retry-After header set) Instagram error 9004 means your file URL is not publicly accessible — re-host it on CurlyFlies and pass the curlyflies.com URL instead.