2026-10-19 11:03:16 +02:00
{
2026-10-19 12:49:15 +02:00
"description" : "Scripted flows (CONTRACT.md section 8). The format, steps, expectations and captures are defined in conformance/FLOWS.md. Everything a run needs is in this file, scenarios.json and statute.json." ,
"statute" : "statute.json" ,
"peers" : {
"callback-1" : "6d61732d6d6f6465732d76302e312d746573742d6b65792d63616c6c6261636b31" ,
"cms-1" : "6d61732d6d6f6465732d76302e312d746573742d6b65792d636d732d312d2d2d2d" ,
"gateway" : "6d61732d6d6f6465732d76302e312d746573742d6b65792d676174657761792d2d"
} ,
"setups" : {
"master" : {
"roles" : {
"ticket" : {
"role" : "master" ,
"modes" : {
"callback" : {
"peer" : "callback-1"
}
} ,
"fields" : [
"phone" ,
"time" ,
"scenario"
] ,
"timeout" : 3600
}
}
} ,
"slave" : {
"roles" : {
"case" : {
"role" : "slave" ,
"master_peer" : "cms-1" ,
"autonomous_send" : false ,
"transfers" : "block"
}
}
} ,
"slave-webhook" : {
"roles" : {
"case" : {
"role" : "slave" ,
"master_peer" : "cms-1" ,
"autonomous_send" : false ,
"transfers" : {
"webhook" : "<mode:callback-1>" ,
"peer" : "callback-1"
}
}
}
}
} ,
2026-10-19 11:03:16 +02:00
"flows" : [
{
"id" : "m-001" ,
2026-10-19 12:49:15 +02:00
"setup" : "master" ,
2026-10-19 11:03:16 +02:00
"title" : "callback completes" ,
"scenario" : "completed" ,
"steps" : [
{
"mail" : {
"to" : "ticket!callback" ,
"text" : "Phone: +49 30 1234567\nTime: tomorrow 10:00"
} ,
"expect" : {
2026-10-19 12:49:15 +02:00
"event_accepted" : true ,
"mode_received" : 1
2026-10-19 11:03:16 +02:00
}
} ,
{
"mode" : "run" ,
"expect" : {
2026-10-19 12:49:15 +02:00
"mode_statuses" : [
2026-10-19 11:03:16 +02:00
"scheduled" ,
"running" ,
"completed"
] ,
"last_mail_contains" : "has taken place"
}
}
]
} ,
{
"id" : "m-002" ,
2026-10-19 12:49:15 +02:00
"setup" : "master" ,
2026-10-19 11:03:16 +02:00
"title" : "negotiation over email" ,
"scenario" : "negotiate" ,
"steps" : [
{
"mail" : {
"to" : "ticket!callback" ,
"text" : "Please call me."
} ,
"expect" : {
2026-10-19 12:49:15 +02:00
"event_accepted" : true
2026-10-19 11:03:16 +02:00
}
} ,
{
"mode" : "run" ,
"expect" : {
2026-10-19 12:49:15 +02:00
"mode_statuses" : [
2026-10-19 11:03:16 +02:00
"negotiating"
] ,
"last_mail_contains" : "Phone: ..."
}
} ,
{
"mail" : {
"to" : "ticket={case_id}" ,
"text" : "Phone: +49 30 1234567\nTime: tomorrow 10:00"
} ,
"expect" : {
2026-10-19 12:49:15 +02:00
"event_accepted" : true ,
"mode_received_last" : "handoff.update"
2026-10-19 11:03:16 +02:00
}
} ,
{
"mode" : "run" ,
"expect" : {
2026-10-19 12:49:15 +02:00
"mode_statuses" : [
2026-10-19 11:03:16 +02:00
"negotiating" ,
"scheduled" ,
"running" ,
"completed"
] ,
"last_mail_contains" : "has taken place"
}
}
]
} ,
{
"id" : "m-003" ,
2026-10-19 12:49:15 +02:00
"setup" : "master" ,
2026-10-19 11:03:16 +02:00
"title" : "no answer, retried, completed" ,
"scenario" : "no-answer-retry" ,
"steps" : [
{
"mail" : {
"to" : "ticket!callback" ,
"text" : "Phone: +49 30 1234567\nTime: tomorrow 10:00"
}
} ,
{
"mode" : "run" ,
"expect" : {
2026-10-19 12:49:15 +02:00
"mode_statuses" : [
2026-10-19 11:03:16 +02:00
"scheduled" ,
"attempted" ,
"scheduled" ,
"running" ,
"completed"
]
}
}
]
} ,
{
"id" : "m-004" ,
2026-10-19 12:49:15 +02:00
"setup" : "master" ,
2026-10-19 11:03:16 +02:00
"title" : "no answer twice, failed" ,
"scenario" : "no-answer-fail" ,
"steps" : [
{
"mail" : {
"to" : "ticket!callback" ,
"text" : "Phone: +49 30 1234567\nTime: tomorrow 10:00"
}
} ,
{
"mode" : "run" ,
"expect" : {
2026-10-19 12:49:15 +02:00
"mode_statuses" : [
2026-10-19 11:03:16 +02:00
"scheduled" ,
"attempted" ,
"scheduled" ,
"attempted" ,
"failed"
] ,
"last_mail_contains" : "did not succeed"
}
}
]
} ,
{
"id" : "m-005" ,
2026-10-19 12:49:15 +02:00
"setup" : "master" ,
2026-10-19 11:03:16 +02:00
"title" : "refused out of hours" ,
"scenario" : "out-of-hours" ,
"steps" : [
{
"mail" : {
"to" : "ticket!callback" ,
"text" : "Phone: +49 30 1234567\nTime: tomorrow 10:00"
}
} ,
{
"mode" : "run" ,
"expect" : {
2026-10-19 12:49:15 +02:00
"mode_statuses" : [
2026-10-19 11:03:16 +02:00
"refused"
] ,
"last_mail_contains" : "out-of-hours"
}
}
]
} ,
{
"id" : "m-006" ,
2026-10-19 12:49:15 +02:00
"setup" : "master" ,
2026-10-19 11:03:16 +02:00
"title" : "email owns the timeout" ,
"scenario" : "silent" ,
"steps" : [
{
"mail" : {
"to" : "ticket!callback" ,
"text" : "Phone: +49 30 1234567\nTime: tomorrow 10:00"
2026-10-19 12:49:15 +02:00
} ,
"expect" : {
"mails_sent" : 1
2026-10-19 11:03:16 +02:00
}
} ,
{
"mode" : "run" ,
"expect" : {
2026-10-19 12:49:15 +02:00
"mode_statuses" : [ ]
2026-10-19 11:03:16 +02:00
}
} ,
{
"advance" : 3599 ,
"expect" : {
2026-10-19 12:49:15 +02:00
"mode_received" : 1 ,
"mails_sent" : 1
2026-10-19 11:03:16 +02:00
}
} ,
{
"advance" : 2 ,
"expect" : {
"mode_received_last" : "handoff.cancel" ,
2026-10-19 12:49:15 +02:00
"mails_sent" : 2 ,
2026-10-19 11:03:16 +02:00
"last_mail_contains" : "could not arrange"
}
2026-10-19 12:49:15 +02:00
} ,
{
"advance" : 100000 ,
"expect" : {
"mode_received" : 2 ,
"mails_sent" : 2
}
2026-10-19 11:03:16 +02:00
}
]
} ,
{
"id" : "m-007" ,
2026-10-19 12:49:15 +02:00
"setup" : "master" ,
2026-10-19 11:03:16 +02:00
"title" : "mode not enabled by the statute" ,
"scenario" : "completed" ,
"steps" : [
{
"mail" : {
"to" : "ticket!chat" ,
"text" : "hi"
} ,
"expect" : {
2026-10-19 12:49:15 +02:00
"event_accepted" : true ,
2026-10-19 11:03:16 +02:00
"mode_received" : 0 ,
2026-10-19 12:49:15 +02:00
"mails_sent" : 1
2026-10-19 11:03:16 +02:00
}
}
]
} ,
{
"id" : "m-008" ,
2026-10-19 12:49:15 +02:00
"setup" : "master" ,
2026-10-19 11:03:16 +02:00
"title" : "unauthenticated sender gets no mail" ,
"scenario" : "negotiate" ,
"steps" : [
{
"mail" : {
"to" : "ticket!callback" ,
"text" : "call me" ,
"dmarc" : "fail"
} ,
"expect" : {
2026-10-19 12:49:15 +02:00
"event_accepted" : true
2026-10-19 11:03:16 +02:00
}
} ,
{
"mode" : "run" ,
"expect" : {
2026-10-19 12:49:15 +02:00
"mode_statuses" : [
2026-10-19 11:03:16 +02:00
"negotiating"
] ,
2026-10-19 12:49:15 +02:00
"mails_sent" : 0
2026-10-19 11:03:16 +02:00
}
}
]
} ,
{
"id" : "s-001" ,
2026-10-19 12:49:15 +02:00
"setup" : "slave" ,
2026-10-19 11:03:16 +02:00
"title" : "master issues the id; email sends only on instruction" ,
2026-10-19 12:49:15 +02:00
"scenario" : "completed" ,
2026-10-19 11:03:16 +02:00
"steps" : [
{
"mail" : {
"to" : "case" ,
"text" : "My order is late"
} ,
"expect" : {
2026-10-19 12:49:15 +02:00
"event_accepted" : true ,
2026-10-19 11:03:16 +02:00
"master_received" : {
"case.intake" : 1
} ,
2026-10-19 12:49:15 +02:00
"mails_sent" : 0
2026-10-19 11:03:16 +02:00
}
} ,
{
2026-10-19 12:49:15 +02:00
"master" : {
"command" : "assign" ,
"resource_id" : "EXT-100"
} ,
2026-10-19 11:03:16 +02:00
"expect" : {
2026-10-19 12:49:15 +02:00
"response_ok" : true ,
2026-10-19 11:03:16 +02:00
"case_address" : "case=EXT-100@modes.example"
}
} ,
{
"mail" : {
"to" : "case={case_id}" ,
"text" : "more"
} ,
"expect" : {
"master_received" : {
"case.message" : 1
} ,
2026-10-19 12:49:15 +02:00
"mails_sent" : 0
2026-10-19 11:03:16 +02:00
}
} ,
{
"master" : {
"command" : "send" ,
"in_reply_to" : "{message_ref}" ,
"body" : "We are on it."
} ,
"expect" : {
2026-10-19 12:49:15 +02:00
"response_ok" : true ,
"mails_sent" : 1
2026-10-19 11:03:16 +02:00
}
}
]
} ,
{
"id" : "s-002" ,
2026-10-19 12:49:15 +02:00
"setup" : "slave" ,
2026-10-19 11:03:16 +02:00
"title" : "principle 10 refuses a master send" ,
2026-10-19 12:49:15 +02:00
"scenario" : "completed" ,
2026-10-19 11:03:16 +02:00
"steps" : [
{
"mail" : {
"to" : "case" ,
"text" : "hi" ,
"dmarc" : "fail"
}
} ,
{
2026-10-19 12:49:15 +02:00
"master" : {
"command" : "assign" ,
"resource_id" : "EXT-100"
}
2026-10-19 11:03:16 +02:00
} ,
{
"master" : {
"command" : "send" ,
"in_reply_to" : "{message_ref}" ,
"body" : "x"
} ,
"expect" : {
2026-10-19 12:49:15 +02:00
"response_ok" : false ,
"response_reason" : "unauthenticated-sender" ,
"mails_sent" : 0
2026-10-19 11:03:16 +02:00
}
}
]
} ,
{
"id" : "s-003" ,
2026-10-19 12:49:15 +02:00
"setup" : "slave" ,
2026-10-19 11:03:16 +02:00
"title" : "transfer blocked" ,
2026-10-19 12:49:15 +02:00
"scenario" : "completed" ,
2026-10-19 11:03:16 +02:00
"steps" : [
{
"mail" : {
"to" : "case!callback" ,
"text" : "Phone: +49 30 1234567\nTime: tomorrow 10:00"
2026-10-19 12:49:15 +02:00
} ,
"expect" : {
"master_received" : {
"case.intake" : 1
}
2026-10-19 11:03:16 +02:00
}
} ,
{
2026-10-19 12:49:15 +02:00
"master" : {
"command" : "assign" ,
"resource_id" : "EXT-100"
} ,
2026-10-19 11:03:16 +02:00
"expect" : {
"master_received" : {
"handoff.requested" : 1 ,
"handoff.status" : 1
} ,
2026-10-19 12:49:15 +02:00
"relayed" : [
"refused"
] ,
2026-10-19 11:03:16 +02:00
"mode_received" : 0
}
}
]
} ,
{
"id" : "s-004" ,
2026-10-19 12:49:15 +02:00
"setup" : "slave-webhook" ,
2026-10-19 11:03:16 +02:00
"title" : "transfer to the primed webhook; statuses relayed" ,
"scenario" : "completed" ,
"steps" : [
{
"mail" : {
"to" : "case!callback" ,
"text" : "Phone: +49 30 1234567\nTime: tomorrow 10:00"
}
} ,
{
2026-10-19 12:49:15 +02:00
"master" : {
"command" : "assign" ,
"resource_id" : "EXT-100"
} ,
2026-10-19 11:03:16 +02:00
"expect" : {
"mode_received" : 1
}
} ,
{
"mode" : "run" ,
"expect" : {
"relayed" : [
"scheduled" ,
"running" ,
"completed"
] ,
2026-10-19 12:49:15 +02:00
"mails_sent" : 0
2026-10-19 11:03:16 +02:00
}
} ,
{
"advance" : 1000000 ,
"expect" : {
2026-10-19 12:49:15 +02:00
"mode_received" : 1
2026-10-19 11:03:16 +02:00
}
}
]
} ,
{
"id" : "s-005" ,
2026-10-19 12:49:15 +02:00
"setup" : "slave-webhook" ,
2026-10-19 11:03:16 +02:00
"title" : "the master owns the timeout" ,
"scenario" : "silent" ,
"steps" : [
{
"mail" : {
"to" : "case!callback" ,
"text" : "Phone: +49 30 1234567\nTime: tomorrow 10:00"
}
} ,
{
2026-10-19 12:49:15 +02:00
"master" : {
"command" : "assign" ,
"resource_id" : "EXT-100"
}
2026-10-19 11:03:16 +02:00
} ,
{
"mode" : "run" ,
"expect" : {
"relayed" : [ ]
}
} ,
2026-10-19 12:49:15 +02:00
{
"advance" : 1000000 ,
"expect" : {
"mode_received" : 1
}
} ,
2026-10-19 11:03:16 +02:00
{
"master" : {
"command" : "cancel" ,
"handoff_id" : "{handoff_id}"
} ,
"expect" : {
2026-10-19 12:49:15 +02:00
"response_ok" : true ,
2026-10-19 11:03:16 +02:00
"mode_received_last" : "handoff.cancel"
}
} ,
{
"mode" : "run" ,
"expect" : {
"relayed" : [
"cancelled"
]
}
}
]
2026-10-19 11:25:24 +02:00
} ,
2026-10-19 12:49:15 +02:00
{
"id" : "s-006" ,
"setup" : "slave" ,
"title" : "over the cap a master send is held" ,
"scenario" : "completed" ,
"steps" : [
{
"mail" : {
"to" : "case" ,
"text" : "hi"
}
} ,
{
"master" : {
"command" : "assign" ,
"resource_id" : "EXT-100"
}
} ,
{
"master" : {
"command" : "send" ,
"in_reply_to" : "{message_ref}" ,
"body" : "one"
} ,
"expect" : {
"response_ok" : true ,
"mails_sent" : 1
}
} ,
{
"master" : {
"command" : "send" ,
"in_reply_to" : "{message_ref}" ,
"body" : "two"
} ,
"expect" : {
"response_ok" : false ,
"response_reason" : "send-cap-reached" ,
"response_held" : true ,
"mails_sent" : 1
}
}
] ,
"max_sent" : 1
} ,
2026-10-19 11:25:24 +02:00
{
"id" : "a-001" ,
2026-10-19 12:49:15 +02:00
"setup" : "slave" ,
2026-10-19 11:25:24 +02:00
"title" : "unsigned master command refused" ,
2026-10-19 12:49:15 +02:00
"scenario" : "completed" ,
2026-10-19 11:25:24 +02:00
"steps" : [
{
"mail" : {
"to" : "case" ,
"text" : "hi"
}
} ,
{
2026-10-19 12:49:15 +02:00
"master" : {
"command" : "assign" ,
"resource_id" : "EXT-100"
}
2026-10-19 11:25:24 +02:00
} ,
{
"inject" : {
"endpoint" : "master" ,
"sign" : "none" ,
"message" : {
"type" : "mail.send" ,
"case" : "current" ,
"in_reply_to" : "{message_ref}" ,
"body" : "hello"
}
} ,
"expect" : {
"http_status" : 401 ,
"error" : "unauthenticated-peer" ,
2026-10-19 12:49:15 +02:00
"mails_sent" : 0
2026-10-19 11:25:24 +02:00
}
}
]
} ,
{
"id" : "a-002" ,
2026-10-19 12:49:15 +02:00
"setup" : "slave" ,
2026-10-19 11:25:24 +02:00
"title" : "a mode cannot assign case ids" ,
2026-10-19 12:49:15 +02:00
"scenario" : "completed" ,
2026-10-19 11:25:24 +02:00
"steps" : [
{
"mail" : {
"to" : "case" ,
"text" : "hi"
}
} ,
{
"inject" : {
"endpoint" : "master" ,
"as" : "callback-1" ,
"message" : {
"type" : "case.assign" ,
"intake_ref" : "{intake_ref}" ,
"resource_id" : "EVIL-1"
}
} ,
"expect" : {
"http_status" : 403 ,
"error" : "wrong-peer"
}
} ,
{
2026-10-19 12:49:15 +02:00
"master" : {
"command" : "assign" ,
"resource_id" : "EXT-100"
} ,
2026-10-19 11:25:24 +02:00
"expect" : {
"case_address" : "case=EXT-100@modes.example"
}
}
]
} ,
{
"id" : "a-003" ,
2026-10-19 12:49:15 +02:00
"setup" : "slave" ,
2026-10-19 11:25:24 +02:00
"title" : "webhook to a peer email has no key for" ,
2026-10-19 12:49:15 +02:00
"scenario" : "completed" ,
2026-10-19 11:25:24 +02:00
"steps" : [
{
"mail" : {
"to" : "case" ,
"text" : "hi"
}
} ,
{
2026-10-19 12:49:15 +02:00
"master" : {
"command" : "assign" ,
"resource_id" : "EXT-100"
}
2026-10-19 11:25:24 +02:00
} ,
{
"inject" : {
"endpoint" : "master" ,
"as" : "cms-1" ,
"message" : {
"type" : "case.policy" ,
"case" : "current" ,
"transfers" : {
"webhook" : "http://attacker.example/h" ,
"peer" : "mallory"
}
}
} ,
"expect" : {
"http_status" : 400 ,
"error" : "unknown-peer"
}
}
]
} ,
{
"id" : "a-004" ,
2026-10-19 12:49:15 +02:00
"setup" : "slave" ,
2026-10-19 11:25:24 +02:00
"title" : "replayed master command" ,
2026-10-19 12:49:15 +02:00
"scenario" : "completed" ,
2026-10-19 11:25:24 +02:00
"steps" : [
{
"mail" : {
"to" : "case" ,
"text" : "hi"
}
} ,
{
2026-10-19 12:49:15 +02:00
"master" : {
"command" : "assign" ,
"resource_id" : "EXT-100"
}
2026-10-19 11:25:24 +02:00
} ,
{
"inject" : {
"endpoint" : "master" ,
"as" : "cms-1" ,
"sign" : "stale" ,
"message" : {
"type" : "mail.send" ,
"case" : "current" ,
"in_reply_to" : "{message_ref}" ,
"body" : "hello"
}
} ,
"expect" : {
"http_status" : 401 ,
"error" : "replayed" ,
2026-10-19 12:49:15 +02:00
"mails_sent" : 0
2026-10-19 11:25:24 +02:00
}
}
]
} ,
{
"id" : "a-005" ,
2026-10-19 12:49:15 +02:00
"setup" : "slave" ,
2026-10-19 11:25:24 +02:00
"title" : "master command signed with the wrong key" ,
2026-10-19 12:49:15 +02:00
"scenario" : "completed" ,
2026-10-19 11:25:24 +02:00
"steps" : [
{
"mail" : {
"to" : "case" ,
"text" : "hi"
}
} ,
{
2026-10-19 12:49:15 +02:00
"master" : {
"command" : "assign" ,
"resource_id" : "EXT-100"
}
2026-10-19 11:25:24 +02:00
} ,
{
"inject" : {
"endpoint" : "master" ,
"as" : "cms-1" ,
"sign" : "wrong-key" ,
"message" : {
"type" : "mail.send" ,
"case" : "current" ,
"in_reply_to" : "{message_ref}" ,
"body" : "hello"
}
} ,
"expect" : {
"http_status" : 401 ,
"error" : "unauthenticated-peer" ,
2026-10-19 12:49:15 +02:00
"mails_sent" : 0
2026-10-19 11:25:24 +02:00
}
} ,
{
"inject" : {
"endpoint" : "master" ,
"as" : "cms-1" ,
"message" : {
"type" : "mail.send" ,
"case" : "current" ,
"in_reply_to" : "{message_ref}" ,
"body" : "hello"
}
} ,
"expect" : {
"http_status" : 200 ,
2026-10-19 12:49:15 +02:00
"mails_sent" : 1
2026-10-19 11:25:24 +02:00
}
}
]
} ,
{
"id" : "a-006" ,
2026-10-19 12:49:15 +02:00
"setup" : "master" ,
2026-10-19 11:25:24 +02:00
"title" : "statuses only from the handoff's peer, at its URL" ,
"scenario" : "silent" ,
"steps" : [
{
"mail" : {
"to" : "ticket!callback" ,
"text" : "Phone: +49 30 1234567\nTime: tomorrow 10:00"
} ,
"expect" : {
2026-10-19 12:49:15 +02:00
"mails_sent" : 1
2026-10-19 11:25:24 +02:00
}
} ,
{
"inject" : {
"endpoint" : "status" ,
"as" : "cms-1" ,
"message" : {
"type" : "handoff.status" ,
"case" : "current" ,
2026-10-19 12:49:15 +02:00
"handoff_id" : "{handoff_id}" ,
2026-10-19 11:25:24 +02:00
"seq" : 1 ,
"status" : "completed"
}
} ,
"expect" : {
"http_status" : 403 ,
"error" : "wrong-peer"
}
} ,
{
"inject" : {
"endpoint" : "status" ,
"as" : "callback-1" ,
"token" : "bogus" ,
"message" : {
"type" : "handoff.status" ,
"case" : "current" ,
2026-10-19 12:49:15 +02:00
"handoff_id" : "{handoff_id}" ,
2026-10-19 11:25:24 +02:00
"seq" : 1 ,
"status" : "completed"
}
} ,
"expect" : {
"http_status" : 404 ,
"error" : "unknown-handoff" ,
2026-10-19 12:49:15 +02:00
"mails_sent" : 1
2026-10-19 11:25:24 +02:00
}
} ,
{
"inject" : {
"endpoint" : "status" ,
"as" : "callback-1" ,
"message" : {
"type" : "handoff.status" ,
"case" : "current" ,
2026-10-19 12:49:15 +02:00
"handoff_id" : "{handoff_id}" ,
2026-10-19 11:25:24 +02:00
"seq" : 1 ,
"status" : "scheduled" ,
"at" : "x"
}
} ,
"expect" : {
"http_status" : 200
}
}
]
} ,
{
"id" : "a-007" ,
2026-10-19 12:49:15 +02:00
"setup" : "master" ,
2026-10-19 11:25:24 +02:00
"title" : "only the gateway posts events" ,
2026-10-19 12:49:15 +02:00
"scenario" : "completed" ,
2026-10-19 11:25:24 +02:00
"steps" : [
{
"inject" : {
"endpoint" : "events" ,
"as" : "callback-1" ,
"message" : {
"type" : "x"
}
} ,
"expect" : {
"http_status" : 403 ,
"error" : "wrong-peer"
}
}
]
2026-10-19 11:03:16 +02:00
}
]
}