Rust SDK · Event shape

The event model is a set of structs (serde-derived):

pub struct Event {
    pub id: String,                        // uuid v4; auto-generated
    pub tenant_id: String,                 // optional; partition by end-customer
    pub occurred_at: DateTime<Utc>,        // auto-populated
    pub actor: Actor,                      // who caused the event
    pub action: String,                    // dotted verb, e.g. "user.lock"
    pub target: Target,                    // what was acted on
    pub metadata: BTreeMap<String, Value>, // freeform context
    pub origin: Origin,                    // IP, user-agent, request ID
    pub outcome: Outcome,                  // serialized to the wire field "result"
    pub change: Option<Change>,            // before/after for mutations
    pub idempotency_key: String,           // optional dedup key
}

pub struct Actor  { pub r#type: String, pub id: String, pub display_name: String, pub email: String }
pub struct Target { pub r#type: String, pub id: String }
pub struct Outcome { pub status: String, pub code: i32, pub message: Option<Value> } // status: "ok"|"error"|"denied"
pub struct Origin { pub ip: String, pub user_agent: String, pub request_id: String }

Fields use plain values with empty defaults; empty means "not set" and the serializer omits them. Field names are snake_case and match the JSON wire format. The outcome type is named Outcome to avoid clashing with [std::result::Result], but serializes to the wire field result. The JSON payload is byte-compatible with the Go, Node, and Python SDKs.

Metadata helpers and construction:

let mut e = everscribe::event::Event::new("user.lock");
e.with_field("reason", "policy_violation");
e.actor = everscribe::event::Actor { r#type: "user".into(), id: "u1".into(), ..Default::default() };