Make one real URL before you keep reading

Upload a file up to 10 MB. The public URL works immediately, with no password or credit card. Executable files require email verification.

File Upload Webhook: Trigger Automation After Every Upload

· 7 min read

An upload-complete webhook is useful when the next action should happen after the file is stored, not merely after your client sent the request. The event can trigger notifications, database updates, or downstream processing without polling.

With FilePost you can attach one webhook URL to your account. When an API upload finishes, FilePost sends a signed file.uploaded event to that URL and retries delivery until your receiver answers. No polling or scheduled checks are required.

What the event looks like

The payload carries everything your workflow needs to react:

{
  "event": "file.uploaded",
  "file_id": "8f3kX9aQ2v",
  "url": "https://cdn.filepost.dev/8f3kX9aQ2v/report.pdf",
  "size": 48291,
  "content_type": "application/pdf",
  "original_name": "report.pdf",
  "filename_mode": "download",
  "upload_source": "api",
  "expires_at": null,
  "uploaded_at": "2026-08-07T12:34:56+00:00"
}
FieldWhat it tells you
file_id / urlThe stable identifier and public CDN URL you can store or pass on.
size / content_type / original_nameMetadata for routing, validation, or database columns.
upload_sourceWhich client made the upload: api, n8n, zapier, make, or pipedream.
expires_atWhen the file is set to auto-delete, or null for permanent files.

Set it up in three steps

1. Point FilePost at your receiver. The URL must be https:// and is stored per account:

curl -X PUT https://filepost.dev/v1/webhook \
  -H "X-API-Key: $FILEPOST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"webhook_url":"https://your-app.example.com/hooks/filepost"}'

2. Send a test event. This gives you instant feedback instead of waiting for the next upload:

curl -X POST https://filepost.dev/v1/webhook/test \
  -H "X-API-Key: $FILEPOST_API_KEY"
# {"event":"ping","ok":true,"status_code":200,"attempts":1,"error":null}

You can do the same from the dashboard: Webhooks → Save → Test, and it reports your receiver's actual HTTP status.

3. Upload as usual. Every successful upload to /v1/upload or /v1/upload/base64 fires the event. Remove the URL with DELETE /v1/webhook when you want it to stop.

Verify the signature so you know it's really FilePost

Each request includes an X-FilePost-Signature header:

X-FilePost-Signature: sha256=<hex-digest>

The digest is an HMAC-SHA256 of the raw request body, keyed with the API key that performed the upload. In Python:

import hashlib
import hmac

def is_valid_filepost_signature(body: bytes, signature: str, api_key: str) -> bool:
    expected = hmac.new(
        api_key.encode(),
        body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

Verify the signature before trusting the payload, especially if your receiver triggers side effects such as writing to a database or sending messages.

Retries are built in

Delivery runs after the upload response is sent, so a slow receiver never slows down your uploads. FilePost retries up to four times with backoff, and stops as soon as your endpoint answers. A 2xx or 3xx response is success; 5xx errors and network timeouts are retried; permanent errors like a 404 stop the retries so you can fix the URL.

Because retries happen, make your receiver idempotent: use file_id as the dedupe key so one event can never create two database rows or two Slack messages.

Real workflow: notify a Slack channel when a client uploads

In n8n, add a Webhook trigger node with POST and point it at your FilePost webhook URL. The JSON body contains the fields above, so the next node can read body.url, body.original_name, and body.size. Send those to a Slack node and you have a "new file uploaded" notification with a working link, without polling.

In Zapier, use the Webhooks by Zapier → Catch Hook trigger, paste the same URL, and map the returned fields into the next step.

For a custom backend, a minimal FastAPI receiver looks like this:

import os
import hashlib
import hmac

from fastapi import FastAPI, Header, Request, HTTPException

app = FastAPI()

@app.post("/hooks/filepost")
async def filepost_hook(
    request: Request,
    x_filepost_signature: str = Header(default=""),
):
    body = await request.body()
    expected = hmac.new(
        os.environ["FILEPOST_API_KEY"].encode(),
        body,
        hashlib.sha256,
    ).hexdigest()
    if not hmac.compare_digest(f"sha256={expected}", x_filepost_signature):
        raise HTTPException(status_code=401, detail="Bad signature")

    event = await request.json()
    if event["event"] == "file.uploaded":
        # e.g. write event["file_id"], event["url"], event["size"] to your DB
        pass
    return {"received": True}

Included on every plan

Webhooks work on the free plan, so you can build the pattern before you pay anything. The free plan includes 15 uploads per month after email verification. When you outgrow it, the Lite plan at $4/month raises you to 300 uploads, 100 MB per file, and 10 GB of storage without changing how your webhook receiver works.

Get notified when your next upload finishes

Set the webhook in the dashboard, send a test, and your pipeline is live.

Get Your Free API Key

Related workflow guides