molroomolroo
API Reference

Webhook

Register webhooks for server-pushed notifications.

Webhook

Webhooks let you receive server-pushed notifications when important events occur in a session. Instead of polling the API, register a URL and receive real-time HTTP callbacks for proactive actions, emotional overflow, and stage transitions.


Register Webhook

POST /v1/webhook/:sessionId

Register a new webhook endpoint for a session.

Headers

HeaderTypeRequiredDescription
AuthorizationstringYesBearer YOUR_API_KEY
Content-TypestringYesapplication/json

Path Parameters

ParameterTypeDescription
sessionIdstringThe session ID to register for

Request Body

FieldTypeRequiredDescription
urlstringYesThe HTTPS URL to receive webhook payloads
secretstringYesSecret key used for HMAC-SHA256 payload signing
eventsstring[]YesArray of event types to subscribe to

Event Types

EventDescription
proactive_actionCharacter initiated a proactive action
emotional_overflowEmotion intensity exceeded the character's coping threshold
stage_transitionCharacter transitioned to a new soul stage

Response

FieldTypeDescription
idstringUnique webhook identifier
urlstringThe registered URL
eventsstring[]Subscribed event types
created_atstringISO 8601 creation timestamp

Examples

curl

curl -X POST https://api.molroo.io/v1/webhook/session_xyz \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhooks/molroo",
    "secret": "whsec_your_secret_key_here",
    "events": ["proactive_action", "emotional_overflow", "stage_transition"]
  }'

JavaScript

const response = await fetch(
  "https://api.molroo.io/v1/webhook/session_xyz",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      url: "https://your-server.com/webhooks/molroo",
      secret: "whsec_your_secret_key_here",
      events: ["proactive_action", "emotional_overflow", "stage_transition"],
    }),
  }
);
 
const webhook = await response.json();
console.log(webhook.id); // "wh_abc123"

Python

import requests
 
response = requests.post(
    "https://api.molroo.io/v1/webhook/session_xyz",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "url": "https://your-server.com/webhooks/molroo",
        "secret": "whsec_your_secret_key_here",
        "events": ["proactive_action", "emotional_overflow", "stage_transition"],
    },
)
 
webhook = response.json()
print(webhook["id"])  # "wh_abc123"

Errors

CodeStatusDescription
UNAUTHORIZED401Invalid or missing API key
VALIDATION_ERROR400Invalid URL, missing events, etc.
NOT_FOUND404Session not found
RATE_LIMIT429Too many requests

List Webhooks

GET /v1/webhook/:sessionId

List all registered webhooks for a session.

Headers

HeaderTypeRequiredDescription
AuthorizationstringYesBearer YOUR_API_KEY

Path Parameters

ParameterTypeDescription
sessionIdstringThe session ID to query

Response

Returns an array of webhook objects:

FieldTypeDescription
idstringUnique webhook identifier
urlstringThe registered URL
eventsstring[]Subscribed event types
created_atstringISO 8601 creation timestamp

Examples

curl

curl https://api.molroo.io/v1/webhook/session_xyz \
  -H "Authorization: Bearer YOUR_API_KEY"

JavaScript

const response = await fetch(
  "https://api.molroo.io/v1/webhook/session_xyz",
  { headers: { Authorization: "Bearer YOUR_API_KEY" } }
);
 
const webhooks = await response.json();
 
webhooks.forEach((wh) => {
  console.log(`${wh.id}: ${wh.url} (${wh.events.join(", ")})`);
});

Python

import requests
 
response = requests.get(
    "https://api.molroo.io/v1/webhook/session_xyz",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
)
 
webhooks = response.json()
 
for wh in webhooks:
    print(f"{wh['id']}: {wh['url']} ({', '.join(wh['events'])})")

Errors

CodeStatusDescription
UNAUTHORIZED401Invalid or missing API key
NOT_FOUND404Session not found

Delete Webhook

DELETE /v1/webhook/:sessionId/:webhookId

Remove a registered webhook.

Headers

HeaderTypeRequiredDescription
AuthorizationstringYesBearer YOUR_API_KEY

Path Parameters

ParameterTypeDescription
sessionIdstringThe session ID
webhookIdstringThe webhook ID to delete

Response

FieldTypeDescription
deletedbooleantrue if successfully deleted
idstringThe deleted webhook ID

Examples

curl

curl -X DELETE https://api.molroo.io/v1/webhook/session_xyz/wh_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY"

JavaScript

const response = await fetch(
  "https://api.molroo.io/v1/webhook/session_xyz/wh_abc123",
  {
    method: "DELETE",
    headers: { Authorization: "Bearer YOUR_API_KEY" },
  }
);
 
const result = await response.json();
console.log(result.deleted); // true

Python

import requests
 
response = requests.delete(
    "https://api.molroo.io/v1/webhook/session_xyz/wh_abc123",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
)
 
result = response.json()
print(result["deleted"])  # True

Errors

CodeStatusDescription
UNAUTHORIZED401Invalid or missing API key
NOT_FOUND404Webhook or session not found

Verifying Webhook Signatures

All webhook payloads are signed with HMAC-SHA256 using the secret you provided during registration. The signature is included in the X-Molroo-Signature header.

To verify a webhook payload:

JavaScript

import crypto from "crypto";
 
function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");
 
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
 
// In your webhook handler:
app.post("/webhooks/molroo", (req, res) => {
  const signature = req.headers["x-molroo-signature"];
  const rawBody = req.body; // raw string
 
  if (!verifyWebhook(rawBody, signature, "whsec_your_secret_key_here")) {
    return res.status(401).send("Invalid signature");
  }
 
  const event = JSON.parse(rawBody);
  console.log(`Received ${event.type} event for session ${event.session_id}`);
 
  res.status(200).send("OK");
});

Python

import hmac
import hashlib
 
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(signature, expected)
 
# In your webhook handler (Flask example):
@app.route("/webhooks/molroo", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("X-Molroo-Signature")
    raw_body = request.get_data()
 
    if not verify_webhook(raw_body, signature, "whsec_your_secret_key_here"):
        return "Invalid signature", 401
 
    event = request.get_json()
    print(f"Received {event['type']} event for session {event['session_id']}")
 
    return "OK", 200

Webhook Payload Structure

All webhook payloads follow this structure:

{
  "type": "proactive_action",
  "session_id": "session_xyz",
  "timestamp": "2025-01-15T10:30:00Z",
  "data": {
    "action_id": "act_abc123",
    "action_type": "attachment_seeking",
    "message": "Hey, I've been thinking about our last conversation...",
    "priority": "medium"
  }
}