Webhooks
IndReelForge can push real-time notifications to your server when uploads complete or fail. Configure a webhook URL once and receive signed HTTP POST payloads for every event.
How It Works
- Configure your webhook endpoint via
POST /api/webhooks/config. - IndReelForge sends a
POSTrequest to your URL with a JSON payload whenever an upload event occurs. - Every delivery includes an
X-IndReelForge-Signatureheader — verify it to confirm the request is genuine. - Failed deliveries are retried up to 3 times with exponential back-off (0 s → 2 s → 5 s).
Configure Webhook
POST https://api.indreelforge.com/api/webhooks/config
Authorization: Apikey YOUR_API_KEY
Content-Type: application/json
Request body
| Field | Type | Required | Description |
|---|---|---|---|
channels.webhook | boolean | Yes | Enable/disable webhook delivery |
channels.telegram | boolean | No | Enable/disable Telegram notifications |
webhook_url | string | If webhook enabled | Your HTTPS endpoint URL |
telegram_chat_id | string | If telegram enabled | Telegram chat ID |
{
"channels": { "webhook": true, "telegram": false },
"webhook_url": "https://your-server.com/hooks/indreelforge"
}
Response
{
"success": true,
"channels": { "webhook": true, "telegram": false },
"webhook_url": "https://your-server.com/hooks/indreelforge",
"webhook_secret": "a1b2c3d4e5f6..."
}
Save the webhook_secret — you need it to verify request signatures. The secret is generated once and never changes unless you regenerate it.
Security note: The URL must be publicly reachable HTTPS. Private/intranet IPs (RFC 1918, loopback, link-local) are rejected to prevent SSRF attacks.
Get Current Config
GET https://api.indreelforge.com/api/webhooks/config
Authorization: Apikey YOUR_API_KEY
Response
{
"channels": { "webhook": true, "telegram": false },
"webhook_url": "https://your-server.com/hooks/indreelforge",
"telegram_chat_id": null,
"webhook_secret": "a1b2c3d4e5f6..."
}
Send a Test Webhook
Triggers a single test delivery to your configured URL.
POST https://api.indreelforge.com/api/webhooks/test
Authorization: Apikey YOUR_API_KEY
Response
{
"success": true,
"message": "Test webhook sent",
"response_status": 200
}
Event Payloads
All events share a common envelope:
{
"event": "upload.completed",
"upload_id": "abc123",
"user_id": "u_xyz",
"timestamp": "2024-01-15T10:30:00.000Z",
"data": { ... }
}
upload.completed
Fired when an upload finishes processing on all platforms.
{
"event": "upload.completed",
"upload_id": "abc123",
"user_id": "u_xyz",
"timestamp": "2024-01-15T10:30:00.000Z",
"data": {
"title": "My Video",
"upload_type": "video",
"platforms": ["instagram", "youtube", "tiktok"],
"platform_statuses": {
"instagram": "posted",
"youtube": "posted",
"tiktok": "posted"
},
"profile": "my-brand"
}
}
upload.partial
Fired when the upload succeeded on at least one platform but failed on others.
{
"event": "upload.partial",
"upload_id": "abc123",
"user_id": "u_xyz",
"timestamp": "2024-01-15T10:31:00.000Z",
"data": {
"title": "My Video",
"upload_type": "video",
"platforms": ["instagram", "youtube", "tiktok"],
"platform_statuses": {
"instagram": "posted",
"youtube": "posted",
"tiktok": "failed"
},
"profile": "my-brand"
}
}
upload.failed
Fired when an upload cannot be delivered to any platform.
{
"event": "upload.failed",
"upload_id": "abc123",
"user_id": "u_xyz",
"timestamp": "2024-01-15T10:31:00.000Z",
"data": {
"title": "My Video",
"upload_type": "video",
"platforms": ["instagram"],
"error": "Instagram token expired"
}
}
test
Sent when you call POST /api/webhooks/test.
{
"event": "test",
"message": "This is a test webhook from IndReelForge",
"timestamp": "2024-01-15T10:00:00.000Z"
}
Verifying Signatures
Every delivery includes the header:
X-IndReelForge-Signature: sha256=<hex>
The signature is HMAC-SHA256(webhook_secret, raw_request_body). Always verify it before processing the payload.
Node.js
const crypto = require('crypto');
function verifySignature(secret, rawBody, signatureHeader) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
);
}
// Express example
app.post('/hooks/indreelforge', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-indreelforge-signature'];
if (!verifySignature(process.env.WEBHOOK_SECRET, req.body, sig)) {
return res.status(401).send('Invalid signature');
}
const payload = JSON.parse(req.body);
console.log('Event:', payload.event);
res.sendStatus(200);
});
Python
import hmac
import hashlib
def verify_signature(secret: str, raw_body: bytes, signature_header: str) -> bool:
expected = 'sha256=' + hmac.new(
secret.encode(),
raw_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header)
# Flask example
from flask import Flask, request, abort
import json
app = Flask(__name__)
@app.route('/hooks/indreelforge', methods=['POST'])
def webhook():
sig = request.headers.get('X-IndReelForge-Signature', '')
if not verify_signature(WEBHOOK_SECRET, request.data, sig):
abort(401)
payload = request.json
print('Event:', payload['event'])
return '', 200
Important: Use a timing-safe comparison (
crypto.timingSafeEqual/hmac.compare_digest) to prevent timing attacks.
Retry Policy
If your endpoint does not return a 2xx status code within 10 seconds, IndReelForge retries the delivery:
| Attempt | Delay |
|---|---|
| 1 (initial) | 0 s |
| 2 | 2 s |
| 3 | 5 s |
After 3 failed attempts the delivery is marked as failed and recorded in the delivery log. You can inspect failed deliveries via GET /api/webhooks/deliveries.
Endpoint requirements
- Must respond with HTTP
2xxwithin 10 seconds. - Must be a publicly reachable HTTPS URL.
- Redirects are not followed (use the final destination URL directly).
Delivery Logs
GET https://api.indreelforge.com/api/webhooks/deliveries
Authorization: Apikey YOUR_API_KEY
Query parameters
| Parameter | Default | Description |
|---|---|---|
page | 1 | Page number |
limit | 20 | Results per page (max 100) |
Response
{
"deliveries": [
{
"id": "del_abc123",
"upload_id": "abc123",
"event_type": "upload.completed",
"response_status": 200,
"delivered_at": "2024-01-15T10:30:01.000Z",
"created_at": "2024-01-15T10:30:00.000Z"
}
]
}
Best Practices
- Respond quickly — return
200immediately and process the payload asynchronously. This avoids timeouts and unnecessary retries. - Verify every signature — never trust a payload without checking
X-IndReelForge-Signature. - Handle duplicates — retries may deliver the same event more than once. Use
upload_id+eventas an idempotency key. - HTTPS only — HTTP endpoints are rejected in production.