diff --git a/config.sample.toml b/config.sample.toml
index 31f8ac483efc0dc613d5e122334f93f0bf3cc37e..472fea3f14c49c5722019405fbeba9bf1a1ca020 100644
--- a/config.sample.toml
+++ b/config.sample.toml
@@ -20,3 +20,12 @@ tempLocation = "/var/data/buckets/_temp"
 storageLocation = "/var/data/buckets/data"
 quarantineLocation = "/var/data/buckets/_quarantine"
 
+[fileWebhook]
+# URL will receive POST with JSON body containing only a `sha256_hash` key for each NEW file
+enable = false
+url = "http://localhost:3100/scan"
+
+[ratelimiter]
+enable = true
+redisURL = "redis://localhost:6379/0"
+
diff --git a/lib/routes/uploadpomf.go b/lib/routes/uploadpomf.go
index e5aaac4f9b99e758dc0e61eb01291662ce835ed6..8140feac33ea024bb18c01aca3d3d249758a98b4 100644
--- a/lib/routes/uploadpomf.go
+++ b/lib/routes/uploadpomf.go
@@ -1,9 +1,11 @@
 package routes
 
 import (
+	"bytes"
 	"crypto/md5"
 	"crypto/sha256"
 	"encoding/hex"
+	"encoding/json"
 	"io"
 	"net/http"
 	"os"
@@ -68,6 +70,11 @@ type fullResponse struct {
 	Files   []fileResponse `json:"files"`
 }
 
+// fileWebhookRequest represents the data submitted in a file webhook request.
+type fileWebhookRequest struct {
+	SHA256Hash string `json:"sha256_hash"`
+}
+
 // UploadPomf handles Pomf multipart/form-data upload requests.
 func UploadPomf(associateObjectsWithUser bool) func(http.ResponseWriter, *http.Request) {
 	return func(w http.ResponseWriter, r *http.Request) {
@@ -270,6 +277,27 @@ func UploadPomf(associateObjectsWithUser bool) func(http.ResponseWriter, *http.R
 						Name:        file.Filename,
 					})
 				}
+
+				// Fire fileWebhook in goroutine
+				if viper.GetBool("fileWebhook.enable") {
+					go func() {
+						reqData := fileWebhookRequest{hex.EncodeToString(sha256Bytes)}
+						m, err := json.Marshal(reqData)
+						if err != nil {
+							log.Warn().Err(err).Msg("failed to marshal reqData in fileWebhook goroutine")
+							return
+						}
+						buf := bytes.NewBuffer(m)
+						resp, err := http.Post(viper.GetString("fileWebhook.url"), "application/json", buf)
+						if err != nil {
+							log.Warn().Err(err).Msg("failed to send request in fileWebhook goroutine")
+							return
+						}
+						if resp.StatusCode < 200 || resp.StatusCode > 399 {
+							log.Warn().Msgf("got unexpected status code from fileWebhook url: %v", resp.StatusCode)
+						}
+					}()
+				}
 			} else {
 				// Delete temporary file
 				err = os.Remove(tempPath)
diff --git a/main.go b/main.go
index 066e3c5de6bde3a58b80ba9b23b4f85fd371970c..65d84f1e1867f61649dcdff36ea9417114f1c710 100644
--- a/main.go
+++ b/main.go
@@ -73,6 +73,7 @@ func init() {
 	viper.SetDefault("ratelimiter.listObjectsCost", ratelimiter.ListObjectsCost)
 	viper.SetDefault("ratelimiter.objectCost", ratelimiter.ObjectCost)
 	viper.SetDefault("ratelimiter.deleteObjectCost", ratelimiter.DeleteObjectCost)
+	viper.SetDefault("fileWebhook.enable", false)
 
 	// Load configuration file
 	viper.SetConfigType("toml")
@@ -138,6 +139,9 @@ func init() {
 	if viper.GetBool("ratelimiter.enable") && viper.GetString("ratelimiter.redisURL") == "" {
 		log.Fatal().Msg("Configuration: ratelimiter.redisURL is required when ratelimiter is enabled")
 	}
+	if viper.GetBool("fileWebhook.enable") && viper.GetString("fileWebhook.url") == "" {
+		log.Fatal().Msg("Configuration: fileWebhook.url is required when fileWebhook is enabled")
+	}
 }
 
 func main() {