Docs
Webhooks
Slack and Discord get a ready-made message: the app icon, version, platform and size (or the device, OS and country for installs), release notes, and a button to the install page. Slack hooks are rate limited to about one message a second, so on a busy hook messages wait their turn instead of being dropped. A generic webhook gets the JSON below, signed so you can check it came from AppForShare. Integrations are part of Pro and Team.
Events
build.ready: A build finished processing and its install link works. Data: build.build.failed: An upload could not be turned into a build. Data: upload (id, fileName, error).build.installed: Someone started an install. Data: build, install (device, osVersion, model, country).bandwidth.warning: Your monthly bandwidth reached 80% or 100%. Data: bandwidth (percent, usedBytes, capBytes, month).
The Send test button delivers a ping event with no data.
Request
POST /your/endpoint
Content-Type: application/json
X-AppForShare-Event: build.ready
X-AppForShare-Delivery: 1842
X-AppForShare-Signature: t=1791850000,v1=5f0c...e91a
{
"id": "3b8e6c1e-9d7a-4c55-8f0e-2a3c5d1b7f42",
"event": "build.ready",
"createdAt": "2026-10-12T18:20:00.000Z",
"data": {
"build": {
"id": 31245,
"slug": "Ab3dE9",
"name": "My App",
"platform": "IOS",
"bundleId": "com.example.myapp",
"version": "2.4.0",
"versionCode": "118",
"sizeBytes": 48211968,
"installUrl": "https://appforshare.io/i/Ab3dE9",
"iconUrl": "https://appforshare.io/api/i/Ab3dE9/icon",
"releaseNotes": "Fixes the login crash",
"createdAt": "2026-10-12T18:19:58.000Z"
}
}
}Checking the signature
The signature is HMAC-SHA256 of the timestamp, a dot and the raw body, keyed with the signing secret shown when you add the webhook.
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody = the request body exactly as received, before JSON parsing.
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // older than 5 minutes
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const got = Buffer.from(parts.v1 ?? '', 'hex');
return got.length === 32 && timingSafeEqual(got, Buffer.from(expected, 'hex'));
}Delivery
- Answer with any 2xx status within 10 seconds. Redirects are not followed.
- Failed deliveries are retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours.
- A 404 or 410 answer stops retries for that delivery.
- After 15 failures in a row the webhook is switched off and we email you.
- A retry sends the same body with the same event id; use the id to skip duplicates.
- Only https URLs on port 443 or 8443 that resolve to public addresses are accepted.