Proactive messages

Let a character reach out first — and test it on demand.

A character can start a message on its own — a “cold initiate” after a long silence, or a nudge on a thread the user left unanswered. Proactive sends arrive as ordinary message.created events on the session event log, so you consume them exactly like replies (poll / SSE / signed webhook).

Turn it on

Proactive is off by default. Enable it per session when you create the session:

$curl -X POST https://api.loreos.app/v1/sessions \
> -H "Authorization: Bearer $LOREOS_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{"character": "yura", "external_user_ref": "u_8123", "proactive_enabled": true}'

proactive_enabled is the master on/off for that session. The character’s behavioral_thresholds (set at authoring time) provide the defaults for when and how often it initiates.

Tune when and how often

Override the conditions for a single session with PATCH /v1/sessions/{id}/proactive-controls. Send only the fields you want to change:

$curl -X PATCH https://api.loreos.app/v1/sessions/$SESSION_ID/proactive-controls \
> -H "Authorization: Bearer $LOREOS_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "proactivity_tolerance": 0.8,
> "cold_initiate_enabled": true,
> "cold_initiate_min_idle_minutes": 180,
> "min_gap_between_initiate_minutes": 240,
> "daily_initiate_cap": 3
> }'
FieldMeaning
proactivity_tolerance (0–1)How willing the character is to initiate at all. 0 = never; 1 = most eager. The master gate — at 0 nothing fires regardless of the rest.
cold_initiate_enabledAllow starting a FRESH conversation after a long silence.
session_resume_enabledAllow nudging an unanswered thread back to life.
cold_initiate_min_idle_minutes (5–1440)Minimum silence before a cold initiate may fire.
session_resume_min_idle_minutes (0–1440)Minimum silence before a resume nudge may fire.
session_resume_max_unanswered_character_turns (0–20)Stop resuming after this many unanswered character turns (anti-nag).
min_gap_between_initiate_minutes (0–1440)Minimum gap between any two proactive messages (rate limit).
daily_initiate_cap (0–50)Maximum proactive messages per day for this session.

The full request schema (with these descriptions) is in GET /v1/openapi.json.

Receiving proactive

Proactive is not a separate channel — it lands on the same event log as replies. But how you consume it matters more here, because proactive fires at an unpredictable time (no user action precedes it):

  • Managed channel (e.g. Telegram) — LoreOS delivers the message straight to the end-user. Zero delivery code; the most convenient option when your surface is a managed channel.
  • Signed webhook — recommended for custom apps. Register POST /v1/sessions/{id}/channels {url, secret}; LoreOS then pushes every event (proactive included) to your URL, signed (x-auto-dating-signature: sha256=HMAC(secret, body)), at-least-once with retry, dedupe on x-auto-dating-event-id. You’re notified the moment a proactive message fires — no polling idle sessions. The body is {"event": {…}} — the same event shape the events endpoint returns.
  • Polling / SSE — fine for reply-time, but a poor fit for proactive: you’d have to poll every idle session indefinitely (or hold a stream open) to catch an unpredictable send. Use them as a fallback, not the primary proactive path.

Can a proactive message include a selfie?

Yes. A proactive message runs through the same engine as a reply, so it can attach a selfie (delivered as an image.ready event, just like a reply image) when the moment fits. Control it with the character’s behavioral_thresholds at POST /v1/characters (typed, with per-field descriptions in the schema):

  • image_proactivity (0–1) — how eagerly the character sends images at all.
  • selfie_comfort (0–1) — how comfortable it is sending self-portraits.
  • daily_image_cap (0–20) — max images per day.

The character also needs an identity visual asset for a selfie to be generated. The decision is contextual: a cold-initiate intro is usually plain text, and a selfie tends to appear once there’s rapport or when it reads as natural — the same way reply selfies work.

Test it now (testflight)

You don’t have to wait for the idle timers. Force ONE proactive message immediately:

$curl -X POST https://api.loreos.app/v1/sessions/$SESSION_ID/proactive-test \
> -H "Authorization: Bearer $LOREOS_API_KEY"

This bypasses the idle / cap / tolerance gating and makes the character send a cold-initiate message now. It returns a run_ref; the message lands as a message.created event within a few seconds — poll GET /v1/sessions/{id}/events?since=<cursor> (or your webhook) to receive it.

proactive-test is for development. Real proactive sends fire on the schedule, governed by the controls above. The test endpoint ignores those so you can verify delivery and tune the character’s voice in one shot.