Usage
You can configure webhooks in your Developer Portal.
Retry logic
For every failed web hook delivery (response code different from 200
, 201
, 204
) Filestack will retry webhook delivery three times:
- in 5 minutes
- in 30 minutes
- in 12 hours
After that webhook is marked as not
delivered.
Events and Messages
The following are examples of all event payloads that your application can receive.
Upload
Event triggers anytime a file is uploaded to your application.
{
"id": 30813791,
"action": "fp.upload",
"timestamp": 1521733983,
"text": {
"container": "filestack-uploads-persist-production",
"url": "https://cdn.filestackcontent.com/HANDLE",
"filename": "FILENAME",
"client": "Computer",
"key": "S3_KEY",
"type": "MIMETYPE",
"status": "Stored",
"size": 158584
}
}
Convert
Event is triggered upon any image or document conversion.
{
"id": 30814778,
"action": "fp.converse",
"timestamp": 1521734642,
"text": {
"url": "/CONVERSION_TASKS/HANDLE",
"link": {
"url": "https://www.filestackapi.com/api/file/HANDLE",
"handle": "HANDLE",
"filename": "FILENAME",
"mimetype": "MIMETYPE",
"path": "FILESTACK_S3_PATH",
"provider": "internal_v2",
"size": 158584
}
}
}
Overwrite
Event is triggered upon any file overwrite.
{
"id": 30815340,
"action": "fp.overwrite",
"timestamp": 1521735062,
"text": {
"mimetype": "MIMETYPE",
"url": "https://cdn.filestackcontent.com/HANDLE",
"isWriteable": true,
"filename": "FILENAME",
"client": "Computer",
"size": 436688
}
}
Delete
Event is triggered upon any file deletion
{
"id": 30814508,
"action": "fp.delete",
"timestamp": 1521734463,
"text": {
"url": "https://cdn.filestackcontent.com/HANDLE"
}
}
Workflow
Event is triggered for every workflow job execution. This example includes results of the virus detection task.
{
"id": 61380634,
"action": "fs.workflow",
"timestamp": 1548214263,
"text": {
"workflow": "687r07d2-5f84-44a0-b20b-1c29a1deb2ab",
"createdAt": "2019-01-23T03:30:55.400729934Z",
"updatedAt": "2019-01-23T03:30:57.437268735Z",
"sources": [
"VJQTvCboRVivWJ2BpKgd"
],
"results": {
"virus_detection_1548213592150": {
"data": {
"infected": false,
"infections_list": []
}
}
},
"status": "Finished"
}
}
Video Conversion
Event is triggered when an audio or video conversion is finished.
{
"status":"completed",
"message":"Done",
"data":{
"thumb":"https://cdn.filestackcontent.com/f1e8V88QDuxzOvtOAq1W",
"thumb100x100":"https://process.filestackapi.com/AhTgLagciQByzXpFGRI0Az/resize=w:100,h:100,f:crop/output=f:jpg,q:66/https://cdn.filestackcontent.com/f1e8V88QDuxzOvtOAq1W",
"thumb200x200":"https://process.filestackapi.com/AhTgLagciQByzXpFGRI0Az/resize=w:200,h:200,f:crop/output=f:jpg,q:66/https://cdn.filestackcontent.com/f1e8V88QDuxzOvtOAq1W",
"thumb300x300":"https://process.filestackapi.com/AhTgLagciQByzXpFGRI0Az/resize=w:300,h:300,f:crop/output=f:jpg,q:66/https://cdn.filestackcontent.com/f1e8V88QDuxzOvtOAq1W",
"url":"https://cdn.filestackcontent.com/VgvFVdvvTkml0WXPIoGn"
},
"metadata":{
"result":{
"audio_channels":2,
"audio_codec":"vorbis",
"audio_sample_rate":44100,
"created_at":"2015/12/21 20:45:19 +0000",
"duration":10587,
"encoding_progress":100,
"encoding_time":8,
"extname":".webm",
"file_size":293459,
"fps":24,
"height":260,
"mime_type":"video/webm",
"started_encoding_at":"2015/12/21 20:45:22 +0000",
"updated_at":"2015/12/21 20:45:32 +0000",
"video_bitrate":221,
"video_codec":"vp8",
"width":300
},
"source":{
"audio_bitrate":125,
"audio_channels":2,
"audio_codec":"aac",
"audio_sample_rate":44100,
"created_at":"2015/12/21 20:45:19 +0000",
"duration":10564,
"extname":".mp4",
"file_size":875797,
"fps":24,
"height":360,
"mime_type":"video/mp4",
"updated_at":"2015/12/21 20:45:32 +0000",
"video_bitrate":196,
"video_codec":"h264",
"width":480
}
},
"timestamp":"1453850583",
"uuid":"638311d89d2bc849563a674a45809b7c"
}
Security
Adding security to webhooks can give you the ability to verify that Filestack was source of the webhook.
In order to validate your webhooks you should follow this procedure:
- Create a secret key for your webhook in your Developer Portal like the following screenshot.
Create a string with the template “[FS-Timestamp].[Content]” where [FS-Timestamp] is the timestamp value in the request header from Filestack, and [Content] is the raw content of the request.
Generate the digital signature of the above-generated string using HMAC-SHA256 algorithm with your webhook secret key from developer portal. You can use any application to sign your string with its secret key using HMAC-SHA256 algorithm. The image below shows an example of generated digital signature.
- Check the generated digital signature with the value of FS-Signature in the header of received webhook. If it matches with FS-Signature that means your received webhook is from Filestack. For more information please check our code snippets.
Webhook Receiver
In order to receive webhooks from Filestack and validate them, use the following Python example using Filestack Python SDK and Flask library.
Requirements:
- filestack-python>=2.7.0,<3.0.0
flask>=1.0.3
from flask import Flask, request, abort from filestack import Client app = Flask(__name__) @app.route('/webhook', methods=['POST']) def webhook(): if request.method == 'POST': resp = Client.verify_webhook_signature('<SECRET>', request.data, dict(request.headers)) if not resp['valid'] and resp['error'] is None: print('Webhook signature is invalid') elif resp['error']: print('Please check input params: {}'.format(resp['error'])) else: print('Webhook is valid and was generated by Filestack') return '', 200 else: abort(400) if __name__ == '__main__': app.run()
Using the provided example in Python you can validate webhooks by using the following cURL command example:
curl -X POST 127.0.0.1:5000/webhook \
-d '{"id": 77099474, "action": "fp.upload", "timestamp": 1559283242, "text": {"container": "container", "url": "https://cdn.filestackcontent.com/handle", "filename": "cloud.png", "client": "Computer", "key": "cloud.png", "type": "image/png", "size": 17548}}' \
-H 'Content-Type: application/json' \
-H 'FS-Timestamp: 1559283242' \
-H 'FS-Signature: 192ff14ef4e56fffe2cead7d0b306fbcb3a227da419f765e20fad10540080753'
In this example, the signature is generated for secret: secret
.
Raw Webhook Check
Node.js
package.json
{ "name": "whdecoder", "version": "1.0.0", "description": "", "main": "index.js", "author": "", "license": "ISC", "dependencies": { "body-parser": "^1.19.0", "express": "^4.17.1" } }
index.js
const express = require('express'); const bodyParser = require('body-parser'); const crypto = require('crypto'); const app = express(); app.use(bodyParser.raw({type: '*/*'})); const port = 5000; const secret = '<SECRET>'; app.post('/webhook', (req, res) => { console.log(`Request Signature: ${req.header('FS-Signature')}`); console.log(`Request Timestamp: ${req.header('FS-Timestamp')}`); console.log(`RawBody ${req.body}`) const hash = crypto .createHmac('sha256', secret) .update(`${req.header('FS-Timestamp')}.${req.body}`) .digest('hex'); console.log(`SIGNATURE IS ${hash === req.header('FS-Signature') ? 'OK' : 'NOT OK!'}`, hash, req.header('FS-Signature')); res.json(req.body) }) app.listen(port, () => console.log(`Example app listening on port ${port}!`))
PHP
<?php
$postdata = file_get_contents("php://input");
$signature = $_SERVER['HTTP_FS_SIGNATURE'];
$timestamp = $_SERVER['HTTP_FS_TIMESTAMP'];
$secret = '<SECRET>';
$res = 'Webhook signature: '.((hash_hmac('sha256', $timestamp . '.' . $postdata, $secret) == $signature) ? "CORRECT" : "INCORRECT");
echo $res;
error_log($res);
For more information please contact Filestack Support
Webhook Examples
You can see some exmaples of generating the string for digital signature in different webhooks.
Workflows webhook
Secret: “SecretSecretSecretAA”
Webhook request content:
{ "id": 1000, "timestamp": 1558123673, "text": { "jobid": "jobid-jobid-jobid-jobid-jobid", "createdAt": "2020-04-26T10:53:02.936164785Z", "workflow": "workflowid-workflowid-workflowid-workflowid", "results": { "border": { "url": "https://cdn.filestackcontent.com/Aaaaaaaaaaaaaaaaaaaaaaz/wf://workflowid-workflowid-workflowid-workflowid/jobid-jobid-jobid-jobid-jobid/aaaaaaaaaaaaaaaaaaaa", "mimetype": "image/png" }, "metadata": { "data": { "size": 64016, "filename": "filename.jpg", "uploaded": 1501181734097.6802, "mimetype": "image/jpeg" } }, "circle": { "url": "https://cdn.filestackcontent.com/Aaaaaaaaaaaaaaaaaaaaaaz/wf://workflowid-workflowid-workflowid-workflowid/jobid-jobid-jobid-jobid-jobid/bbbbbbbbbbbbbbbbbbbb", "mimetype": "image/png" } }, "sources": [ "Handle1", "Handle2" ], "updatedAt": "2020-04-26T10:53:06.524680403Z", "status": "Finished" }, "action": "fs.workflow" }
Signing string:
1559204277.{"id": 1000, "timestamp": 1558123673, "text": {"jobid": "jobid-jobid-jobid-jobid-jobid", "createdAt": "2020-04-26T10:53:02.936164785Z", "workflow": "workflowid-workflowid-workflowid-workflowid", "results": {"border": {"url": "https://cdn.filestackcontent.com/Aaaaaaaaaaaaaaaaaaaaaaz/wf://workflowid-workflowid-workflowid-workflowid/jobid-jobid-jobid-jobid-jobid/aaaaaaaaaaaaaaaaaaaa", "mimetype": "image/png"}, "metadata": {"data": {"size": 64016, "filename": "filename.jpg", "uploaded": 1501181734097.6802, "mimetype": "image/jpeg"}}, "circle": {"url": "https://cdn.filestackcontent.com/Aaaaaaaaaaaaaaaaaaaaaaz/wf://workflowid-workflowid-workflowid-workflowid/jobid-jobid-jobid-jobid-jobid/bbbbbbbbbbbbbbbbbbbb", "mimetype": "image/png"}}, "sources": ["Handle1", "Handle2"], "updatedAt": "2020-04-26T10:53:06.524680403Z", "status": "Finished"}, "action": "fs.workflow"}
Request header:
FS-Signature: a841816d3ad7782ccc07434681d1f19649dc8a3d60cc32b33f6ee0bcc8052272 FS-Timestamp: 1559204277
Upload webhook
Secret: “SecretSecretSecretAA”
Webhook request content:
{ "text": { "url": "https://cdn.filestackcontent.com/Handle", "type": "image/jpeg", "size": 100000, "container": "your-bucket", "key": "kGaeljnga9wkysK6Z_filename.jpg", "filename": "filename.jpg", "status": "Stored", "client": "Computer" }, "timestamp": 1558123673, "id": 1000, "action": "fp.upload" }
Signing string:
1559204382.{"text": {"url": "https://cdn.filestackcontent.com/Handle", "type": "image/jpeg", "size": 100000, "container": "your-bucket", "key": "kGaeljnga9wkysK6Z_filename.jpg", "filename": "filename.jpg", "status": "Stored", "client": "Computer"}, "timestamp": 1558123673, "id": 1000, "action": "fp.upload"}
Request header:
FS-Signature: 4e0cb808e0e4f1ab6cbcf9b38841c7aca09b2b938b40ac719a8fc3ce7c644923 FS-Timestamp: 1559204382
Video Convert webhook
Secret: “SecretSecretSecretAA”
Webhook request content:
{ "uuid": "15c58ienbuenvjd228ba81c11fd1a156", "id": 1000, "metadata": { "result": { "updated_at": "2019/05/17 07:03:23 +0000", "extname": ".mp4", "started_encoding_at": "2019/05/17 07:03:16 +0000", "mime_type": "video/mp4", "width": 270, "audio_sample_rate": 44100, "encoding_time": 6, "file_size": 91697, "audio_channels": 1, "encoding_progress": 100, "created_at": "2019/05/17 07:03:13 +0000", "audio_codec": "aac", "height": 480, "video_bitrate": 225, "audio_bitrate": 129, "duration": 2047, "fps": 24, "video_codec": "h264" }, "source": { "updated_at": "2019/05/17 07:03:23 +0000", "extname": ".mp4", "mime_type": "video/mp4", "width": 270, "audio_sample_rate": 44100, "file_size": 330154, "audio_channels": 1, "created_at": "2019/05/17 07:03:13 +0000", "audio_codec": "aac", "height": 480, "video_bitrate": 311, "audio_bitrate": 128, "duration": 5968, "fps": 24, "video_codec": "h264" } }, "status": "completed", "data": { "thumb": "https://cdn.filestackcontent.com/Handle1", "thumb200x200": "https://process.filestackapi.com/Aaaaaaaaaaaaaaaaaaaaaaz/resize=w:200,h:200,f:crop/output=f:jpg,q:66/https://cdn.filestackcontent.com/Handle1", "thumb300x300": "https://process.filestackapi.com/Aaaaaaaaaaaaaaaaaaaaaaz/resize=w:300,h:300,f:crop/output=f:jpg,q:66/https://cdn.filestackcontent.com/Handle1", "thumb100x100": "https://process.filestackapi.com/Aaaaaaaaaaaaaaaaaaaaaaz/resize=w:100,h:100,f:crop/output=f:jpg,q:66/https://cdn.filestackcontent.com/Handle1", "url": "https://cdn.filestackcontent.com/Handle2" }, "timestamp": "1558123673" }
Signing string:
1559204470.{"uuid": "15c58ienbuenvjd228ba81c11fd1a156", "id": 1000, "metadata": {"result": {"updated_at": "2019/05/17 07:03:23 +0000", "extname": ".mp4", "started_encoding_at": "2019/05/17 07:03:16 +0000", "mime_type": "video/mp4", "width": 270, "audio_sample_rate": 44100, "encoding_time": 6, "file_size": 91697, "audio_channels": 1, "encoding_progress": 100, "created_at": "2019/05/17 07:03:13 +0000", "audio_codec": "aac", "height": 480, "video_bitrate": 225, "audio_bitrate":129, "duration": 2047, "fps": 24, "video_codec": "h264"}, "source": {"updated_at": "2019/05/17 07:03:23 +0000", "extname": ".mp4", "mime_type": "video/mp4", "width": 270, "audio_sample_rate": 44100, "file_size": 330154, "audio_channels": 1, "created_at": "2019/05/17 07:03:13 +0000", "audio_codec": "aac", "height": 480, "video_bitrate": 311, "audio_bitrate": 128, "duration": 5968, "fps": 24, "video_codec": "h264"}}, "status": "completed", "data": {"thumb": "https://cdn.filestackcontent.com/Handle1", "thumb200x200": "https://process.filestackapi.com/Aaaaaaaaaaaaaaaaaaaaaaz/resize=w:200,h:200,f:crop/output=f:jpg,q:66/https://cdn.filestackcontent.com/Handle1", "thumb300x300": "https://process.filestackapi.com/Aaaaaaaaaaaaaaaaaaaaaaz/resize=w:300,h:300,f:crop/output=f:jpg,q:66/https://cdn.filestackcontent.com/Handle1", "thumb100x100": "https://process.filestackapi.com/Aaaaaaaaaaaaaaaaaaaaaaz/resize=w:100,h:100,f:crop/output=f:jpg,q:66/https://cdn.filestackcontent.com/Handle1", "url": "https://cdn.filestackcontent.com/Handle2"}, "timestamp": "1558123673"}
Request header:
FS-Signature: d02afaaaab60786abc68929d812e9985a5d57161dbf10fe2842656f4e90e6c76 FS-Timestamp: 1559204470