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
Log in to your OnlyMonster dashboard
Navigate to API → Webhooks
Enter your HTTPS endpoint URL and click Save
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:
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:
Get the
x-om-webhook-timestampandx-om-webhook-signatureheaders from the requestConstruct the signed content by concatenating the timestamp, a dot (
.), and the raw request body:Compute an HMAC-SHA256 hash of the signed content using your webhook secret
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
payload.account objectAccount-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:
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
chat.messageFires 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):
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):
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_id→ incoming — the fan sent the message to you.from_id !== fan_id→ outgoing — 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:
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
chat.message_sentFires 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):
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
chat.message_errorFires 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):
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
vault.media_upload.createdFires 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):
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
vault.media_upload.updatedFires 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(andmedia_id) on every event and reconcile againstmedia_upload_id.Events for the same
media_upload_idmay arrive out of order. Order them byupdated_atbefore 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-idchanges 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
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-signaturebefore processing any event to ensure authenticity.Respond quickly — Return a
200response 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?