Webhook Integration

Webhooks allow you to receive real-time HTTP notifications when events occur in your OnlyMonster account. Instead of polling for changes, your server receives a POST request with event data as soon as something happens.

Note: Webhook event types are currently in active development. This page will be updated as new events become available.

Setup

  1. Log in to your OnlyMonster dashboard

  2. Navigate to API → Webhooks

  3. Enter your HTTPS endpoint URL and click Save

  4. Copy and securely store the webhook secret

Important: The webhook secret is displayed only once. Save it in a secure location immediately. If you lose it, you will need to create a new webhook.

You can view or delete your active webhook from the same API → Webhooks tab.

Note: It can take up to 1 minute after you save a new webhook before events start being delivered.

Receiving Webhooks

When an event occurs, OnlyMonster sends an HTTP POST request to your configured URL with the following headers:

Header
Description

content-type

Always application/json

x-om-webhook-signature

HMAC-SHA256 hex-encoded signature

x-om-webhook-timestamp

ISO 8601 timestamp of when the event was sent

x-om-webhook-id

UUID unique to this delivery attempt — use it for deduplication if you process retries

Payload Format

The request body is a JSON object with the following structure:

  • type — a string identifying the event (e.g. chat.message, chat.message_sent)

  • payload — an object containing event-specific data

Verifying Signatures

Every webhook request is signed using your secret so you can verify it was sent by OnlyMonster and hasn't been tampered with.

To verify a webhook signature:

  1. Get the x-om-webhook-timestamp and x-om-webhook-signature headers from the request

  2. Construct the signed content by concatenating the timestamp, a dot (.), and the raw request body:

  3. Compute an HMAC-SHA256 hash of the signed content using your webhook secret

  4. Compare the hex-encoded result with x-om-webhook-signature

Node.js / TypeScript

Go

Event Types

All events follow a consistent structure:

The Source column in the field tables indicates whether a value originates from OnlyFans (so it follows OnlyFans formats and lifecycle — IDs match what you'd see in the OF API) or from OnlyMonster (identifiers and metadata generated on our side).

Shared payload.account object

Account-scoped events (e.g. chat.message, vault.media_upload.created, vault.media_upload.updated) include an account object on payload that identifies which account the event belongs to:

Field
Type
Source
Description

account_id

string

OnlyMonster

OnlyMonster account ID — stable across platform reconnects.

platform_account_id

string

OnlyFans

OnlyFans user ID of the connected account (your account, not the fan's).

chat.message

Fires when a chat message is observed on one of your connected OnlyFans accounts. This includes both messages a fan sends to you and messages sent from your account to a fan.

Example:

Fields (payload):

Field
Type
Source
Description

account

object

mixed

Identifies the account this event belongs to. See the shared payload.account table above.

message

object

OnlyFans

The chat message. See payload.message fields below.

Fields (payload.message):

Field
Type
Source
Description

message_id

string

OnlyFans

OnlyFans message ID.

fan_id

string

OnlyFans

OnlyFans user ID of the fan in the conversation (always the other party — never your own account).

from_id

string

OnlyFans

OnlyFans user ID of the sender.

created_at

string (ISO 8601)

OnlyFans

When the message was created on OnlyFans.

text

string | null

OnlyFans

Message text. null for media-only messages.

medias

Record<string, { type: string }>

OnlyFans

Map of attached media keyed by OnlyFans media ID. Each value contains the media type (e.g. "photo", "video", "gif", "audio").

Determining message direction:

The payload does not include an explicit direction field. Compare from_id and fan_id to tell incoming from outgoing:

  • from_id === fan_idincoming — the fan sent the message to you.

  • from_id !== fan_idoutgoing — you sent the message to the fan.

Restored messages:

If our connection to OnlyFans is briefly lost, missed activity is reconciled when the connection is restored. For each chat that had new activity during the outage, we deliver only the most recent message in that chat — intermediate messages sent during the outage are not backfilled. These deliveries include an additional metadata object on the payload:

Field
Type
Source
Description

is_restored

boolean (optional)

OnlyMonster

true when the message was backfilled after a brief disconnect rather than received in real time.

connection_lost_at

string (ISO 8601, optional)

OnlyMonster

When the disconnect started — bounds the period these restored messages cover.

For restored messages, medias is delivered as an empty object ({}) — media metadata is not available from the backfill source.

chat.message_sent

Fires when a message you submitted via the OnlyMonster API is successfully delivered to OnlyFans. This event is only sent for messages you submit through the public API — messages sent by other OnlyMonster features do not trigger this webhook.

Example:

Fields (payload):

Field
Type
Source
Description

send_id

string

OnlyMonster

Identifier returned to you when you submitted the send request — use it to correlate.

account_id

string

OnlyMonster

OnlyMonster account ID the message was sent from.

fan_id

string

OnlyFans

OnlyFans user ID of the recipient.

platform_message_id

string

OnlyFans

OnlyFans message ID assigned to the delivered message. The corresponding chat.message event for this delivery will carry the same value in payload.message.message_id.

chat.message_error

Fires when a message you submitted via the OnlyMonster API fails to be delivered to OnlyFans. As with chat.message_sent, this event is only sent for messages submitted through the public API.

Example:

Fields (payload):

Field
Type
Source
Description

send_id

string

OnlyMonster

Identifier returned to you when you submitted the send request — use it to correlate.

account_id

string

OnlyMonster

OnlyMonster account ID the message was attempted from.

fan_id

string

OnlyFans

OnlyFans user ID of the intended recipient.

status

"restricted" | "failed"

OnlyMonster

restricted — the recipient has blocked or restricted messages. failed — any other delivery failure.

vault.media_upload.created

Fires when a new media upload is created in your OnlyMonster vault. The upload starts in the uploaded state and transitions through processing/exporting states; subsequent transitions are delivered as vault.media_upload.updated events.

Example:

Fields (payload):

Field
Type
Source
Description

media_upload_id

string

OnlyMonster

Stable OnlyMonster identifier for this upload — use it to correlate created and subsequent updated events.

media_id

string | null

OnlyFans

OnlyFans media ID assigned once the upload has been exported to the platform. null until export completes.

status

string (enum)

OnlyMonster

Current upload state. One of uploaded, processing, processed, exporting, exported, failed, unprocessable.

account

object

mixed

Identifies the account this upload belongs to. See the shared payload.account table above.

created_at

string (ISO 8601)

OnlyMonster

When the upload row was created.

updated_at

string (ISO 8601)

OnlyMonster

When the upload row was last persisted. Equal to created_at for a freshly created upload.

Note: Ordering and dedup guidance is shared with vault.media_upload.updated below — read it before storing state.

vault.media_upload.updated

Fires on any persisted change to an existing vault media upload — most commonly a status transition (e.g. processing → processed) or an assignment of media_id once the upload is processed.

Important:

  • Consumers should not assume a specific delta from a single event — read status (and media_id) on every event and reconcile against media_upload_id.

  • Events for the same media_upload_id may arrive out of order. Order them by updated_at before applying — do not flip a stored status backward just because a newer event was received first.

  • Vault events may be delivered more than once (e.g. when a previous attempt's response was lost in flight) — treat (media_upload_id, updated_at) as the dedup key. x-om-webhook-id changes on every retry attempt and is not suitable for cross-retry deduplication.

Example:

Fields (payload): Same as vault.media_upload.created above. updated_at advances on each transition.

Retry & Delivery Behavior

Behavior
Details

Timeout

15 seconds per request

Retries

Up to 3 attempts with 15-second delay

Retry condition

5xx server errors and network failures

No retry

2xx (success), 3xx, 4xx responses

Redirects

Not followed

Your endpoint must respond with an HTTP 2xx status code to acknowledge receipt. Any 5xx response or network failure will trigger a retry.

Backpressure during sustained timeouts

To protect both your endpoint and our delivery pipeline, we apply per-organisation backpressure when your endpoint stops responding entirely. If your endpoint times out (no response within the 15-second window) on several consecutive deliveries, we temporarily pause delivery for ~1 minute. Events that fire during the pause window are dropped — not retried. After the pause, we send a single probe; if it succeeds, normal delivery resumes immediately, otherwise the pause renews.

Backpressure is triggered only by request timeouts. 5xx responses, network errors (e.g. connection refused, DNS), and SSRF/HTTP-blocked targets do not trigger it — they follow the standard retry table above.

If you need a complete history of events, treat webhooks as a real-time signal and reconcile against the corresponding polling endpoints periodically. Backpressure pauses are observable on your side as a gap with no delivery attempts.

Best Practices

  • Verify signatures — Always verify the x-om-webhook-signature before processing any event to ensure authenticity.

  • Respond quickly — Return a 200 response immediately, then process the event asynchronously. Long-running processing may cause timeouts.

  • Use HTTPS — Webhook URLs must use HTTPS. HTTP endpoints are rejected.

  • Secure your secret — The webhook secret is shown only once during setup. Store it in a secrets manager or environment variable.

Last updated

Was this helpful?