Webhooks
Receive real-time notifications for events
Webhooks
Webhooks let you receive real-time HTTP notifications when events happen in your Octopost account. Instead of polling the API for changes, register a webhook URL and Octopost will send a POST request to it whenever a subscribed event occurs.
How Webhooks Work
- You register a webhook endpoint URL and select which events to subscribe to.
- When a subscribed event occurs, Octopost sends an HTTP POST request to your URL with the event payload.
- Your server responds with a
2xxstatus code to acknowledge receipt. - If delivery fails, Octopost retries with exponential backoff.
Events
| Event | Description |
|---|---|
post.published | A post was successfully published to at least one platform. |
post.failed | A post failed to publish on all platforms. |
post.scheduled | A post was scheduled for future publishing. |
account.connected | A new social media account was connected. |
account.disconnected | A social media account was disconnected or its token expired. |
The Webhook Object
{
"id": "wh_abc123",
"url": "https://example.com/webhooks/octopost",
"events": ["post.published", "post.failed"],
"secret": "whsec_abc123def456",
"is_active": true,
"created_at": "2026-04-01T10:00:00Z",
"updated_at": "2026-04-01T10:00:00Z"
}Event Payload
Every webhook delivery sends a JSON payload with this structure:
{
"id": "evt_abc123",
"type": "post.published",
"created_at": "2026-04-03T12:05:00Z",
"data": {
"post": {
"id": "post_def456",
"content": "Hello from Octopost!",
"platforms": ["twitter", "bluesky"],
"status": "published",
"platform_results": {
"twitter": {
"success": true,
"post_id": "1234567890",
"post_url": "https://x.com/user/status/1234567890"
},
"bluesky": {
"success": true,
"post_id": "at://did:plc:abc/app.bsky.feed.post/xyz",
"post_url": "https://bsky.app/profile/user.bsky.social/post/xyz"
}
},
"published_at": "2026-04-03T12:05:00Z"
}
}
}Event-Specific Data
post.published and post.failed: data.post contains the full post object including platform_results.
post.scheduled: data.post contains the post object with scheduled_for set.
account.connected: data.account contains the connected account object.
account.disconnected: data.account contains the disconnected account object with is_active: false.
Managing Webhooks
Create a Webhook
POST /webhooks
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The HTTPS URL to receive webhook deliveries. Must use HTTPS. |
events | string[] | Yes | Array of event types to subscribe to. |
Example
curl -X POST https://api.octopost.ink/v1/webhooks \
-H "Authorization: Bearer oct_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/octopost",
"events": ["post.published", "post.failed"]
}'{
"id": "wh_abc123",
"url": "https://example.com/webhooks/octopost",
"events": ["post.published", "post.failed"],
"secret": "whsec_abc123def456",
"is_active": true,
"created_at": "2026-04-03T16:00:00Z",
"updated_at": "2026-04-03T16:00:00Z"
}The secret is returned only on creation. Store it securely -- you will need it to verify webhook signatures.
List Webhooks
GET /webhooks
curl https://api.octopost.ink/v1/webhooks \
-H "Authorization: Bearer oct_live_abc123"{
"webhooks": [
{
"id": "wh_abc123",
"url": "https://example.com/webhooks/octopost",
"events": ["post.published", "post.failed"],
"is_active": true,
"created_at": "2026-04-03T16:00:00Z",
"updated_at": "2026-04-03T16:00:00Z"
}
]
}Note: The secret is not returned in list or get responses.
Update a Webhook
PUT /webhooks/:id
| Field | Type | Description |
|---|---|---|
url | string | Updated endpoint URL. |
events | string[] | Updated event subscriptions. |
is_active | boolean | Enable or disable the webhook. |
curl -X PUT https://api.octopost.ink/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer oct_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"events": ["post.published", "post.failed", "post.scheduled"]
}'Delete a Webhook
DELETE /webhooks/:id
curl -X DELETE https://api.octopost.ink/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer oct_live_abc123"Returns 204 No Content on success.
Signature Verification
Every webhook delivery includes an X-Octopost-Signature header containing an HMAC-SHA256 signature. Verify this signature to confirm the request came from Octopost and was not tampered with.
The signature is computed over the raw request body using your webhook secret as the key.
Verification Steps
- Extract the
X-Octopost-Signatureheader from the request. - Compute the HMAC-SHA256 of the raw request body using your webhook
secretas the key. - Compare the computed signature to the header value using a constant-time comparison.
Example: Node.js / TypeScript
import crypto from "crypto";
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your webhook handler:
const isValid = verifyWebhookSignature(
rawBody,
request.headers["x-octopost-signature"],
"whsec_abc123def456"
);
if (!isValid) {
return new Response("Invalid signature", { status: 401 });
}Example: Python
import hmac
import hashlib
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)Retry Policy
If your endpoint does not respond with a 2xx status code within 30 seconds, the delivery is considered failed and Octopost retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 12 hours |
After 5 failed retries, the delivery is marked as failed and no further attempts are made. If a webhook consistently fails (10 consecutive failed deliveries across events), the webhook is automatically deactivated and you are notified via email.
You can view delivery history and manually retry failed deliveries from the Octopost Dashboard.
Best Practices
- Respond quickly. Return a
200immediately, then process the event asynchronously. Do not perform long-running work in the webhook handler. - Handle duplicate deliveries. Use the
idfield in the event payload to deduplicate. In rare cases, the same event may be delivered more than once. - Verify signatures. Always verify the
X-Octopost-Signatureheader before trusting the payload. - Use HTTPS. Webhook URLs must use HTTPS. HTTP endpoints are rejected.