Declaring Scenes
Composing and declaring scenes from intention to live execution.
Scenes orchestrate beats across channels toward an intention. Build prerequisites, compose beats, declare, and monitor.
Prerequisites
A scene requires a channel, an audience, and an intention.
const rodeo = new Rodeo({ apiKey: "your-key" });
const sms = await rodeo.channels.create({
name: "sms-outreach",
type: "direct",
tone: { register: "casual", voice: "peer", awareness: "cold", deniability: "none" },
provider: "twilio",
provider_config: { from: "+15551234567" },
sensor_capabilities: ["delivery", "response"],
});
const audience = await rodeo.audiences.create({
type: "individual",
context: { name: "Alex", role: "vendor", region: "Austin" },
bounds: { maxBeatsPerDay: 3, maxBeatsPerWeek: 8, cooldownMinutes: 60 },
});
const intention = await rodeo.intentions.create({
type: "recruit",
description: "Recruit vendor for Austin farmer's market",
success_condition: { contractSigned: true },
alignment: {
audienceBenefit: "Guaranteed booth space and marketing support",
mutuality: "Vendor gets customers, market gets product variety",
},
});Composition
const result = await rodeo.scenes
.build()
.intention(intention.id)
.audience(audience.id)
.arc("approach")
.constraints({ maxBeats: 4, maxDuration: "4h", budget: 25 })
.onDecline("terminate")
.onPositive("continue")
.onNegative("decelerate")
.rule({
condition: { type: "response", metric: "replied", sentiment: "positive" },
action: "accelerate",
})
.beat({
sequence: 1,
channel_id: sms.id,
content: { body: "Hey Alex — we're expanding the Austin market." },
tone: { warmth: "warm", urgency: "none", ask: "implicit" },
timing: { mode: "absolute", at: "2025-01-15T09:00:00-06:00" },
})
.beat({
sequence: 2,
channel_id: sms.id,
content: { body: "Happy to jump on a call whenever works." },
tone: { warmth: "familiar", urgency: "present", ask: "direct" },
timing: { mode: "relative", after: "2h", anchor: "beat:1" },
})
.create();Scene is now in draft status. Nothing has fired.
Declaration
Moves the scene from draft to live:
const declared = await rodeo.scenes.declare(result.scene.id);Two things happen:
- A ledger entry records the intention, audience type, benefit, and scene hash.
- If copilot is enabled, an action card pauses the scene until a human approves.
if (declared.action_card) {
// Scene in "declared" status — awaiting approval
} else {
// Scene in "live" status — beats firing
}Monitoring
Stream events in real-time:
for await (const event of rodeo.scenes.stream(result.scene.id)) {
console.log(event.type, event.data);
}Poll for status:
const scene = await rodeo.scenes.get(result.scene.id);
// scene.scene.status: live | adapting | completed | ...Abort
await rodeo.scenes.abort(result.scene.id);Sets pending beats to skipped, records outcome as aborted in the ledger.