Make one real URL before you keep reading

Upload a file up to 10 MB. FilePost stores it, returns the public CDN URL, and creates your free API key in the same step.

Turn a Webhook Attachment into a Public CDN URL

August 6, 2026 · 6 min read

A webhook can deliver a file as multipart data, a temporary download URL, or a base64 string. None of those formats is automatically a durable public link. The reliable pattern is to resolve the bytes once, upload them to file storage, and pass the returned URL to every downstream step.

FilePost reduces that storage step to one authenticated request. The response includes a public url, a stable file_id, and the file metadata you need for a database row, CRM field, Slack message, or follow-up webhook.

Choose the recipe that matches the webhook payload

Incoming valueWhat your handler doesFilePost endpoint
Multipart attachmentForward the file streamPOST /v1/upload
Temporary file URLDownload, validate, then forward the bytesPOST /v1/upload
Base64 stringForward filename, content type, and base64 data as JSONPOST /v1/upload/base64

Multipart attachment example

This FastAPI handler receives a file and immediately turns it into a FilePost URL:

import os
import httpx
from fastapi import FastAPI, UploadFile

app = FastAPI()

@app.post("/webhooks/file")
async def receive_file(file: UploadFile):
    contents = await file.read()
    async with httpx.AsyncClient(timeout=30) as client:
        response = await client.post(
            "https://filepost.dev/v1/upload",
            headers={"X-API-Key": os.environ["FILEPOST_API_KEY"]},
            files={"file": (file.filename, contents, file.content_type)},
        )
    response.raise_for_status()
    uploaded = response.json()
    return {"public_url": uploaded["url"], "file_id": uploaded["file_id"]}

Temporary source URL example

Do not copy an untrusted URL directly into a server-side request without controls. Allow only expected hosts, reject private and loopback addresses, cap redirects and response size, and validate the content type before forwarding the bytes.

source = await client.get(approved_url, follow_redirects=False)
source.raise_for_status()

uploaded = await client.post(
    "https://filepost.dev/v1/upload",
    headers={"X-API-Key": os.environ["FILEPOST_API_KEY"]},
    files={"file": ("attachment.pdf", source.content, "application/pdf")},
)
uploaded.raise_for_status()
public_url = uploaded.json()["url"]

Base64 JSON example

If the webhook already carries base64 data, keep the handoff as JSON:

curl -X POST https://filepost.dev/v1/upload/base64 \
  -H "X-API-Key: $FILEPOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "report.csv",
    "content_type": "text/csv",
    "data_base64": "bmFtZSx0b3RhbApGaWxlUG9zdCw0Mg=="
  }'

Production checks

Build the storage step once

Start with the live upload above, then copy the matching recipe into your webhook handler.

Get Your Free API Key

Related workflow guides