From c35e2a2d1d1dca967879bb62fb46f1828adf0688 Mon Sep 17 00:00:00 2001
From: Rowan Seymour <rowanseymour@gmail.com>
Date: Fri, 6 Oct 2023 21:26:08 +0000
Subject: [PATCH] Drop support for contact less sessions

---
 flows/actions/add_contact_groups.go           |   7 -
 flows/actions/add_contact_urn.go              |   7 -
 flows/actions/base_test.go                    |  32 ++-
 flows/actions/remove_contact_groups.go        |   7 -
 flows/actions/send_msg.go                     |   5 -
 flows/actions/set_contact_channel.go          |   6 -
 flows/actions/set_contact_field.go            |   5 -
 flows/actions/set_contact_language.go         |   5 -
 flows/actions/set_contact_name.go             |   5 -
 flows/actions/set_contact_status.go           |   6 -
 flows/actions/set_contact_timezone.go         |   5 -
 .../actions/testdata/add_contact_groups.json  |  22 --
 flows/actions/testdata/add_contact_urn.json   |  28 ---
 .../testdata/remove_contact_groups.json       |  22 --
 flows/actions/testdata/send_msg.json          |  44 ----
 .../actions/testdata/set_contact_channel.json |  20 --
 flows/actions/testdata/set_contact_field.json |  37 ----
 .../testdata/set_contact_language.json        |  17 --
 flows/actions/testdata/set_contact_name.json  |  27 ---
 .../actions/testdata/set_contact_status.json  |  17 --
 .../testdata/set_contact_timezone.json        |  17 --
 flows/actions/testdata/transfer_airtime.json  |  47 ----
 flows/actions/transfer_airtime.go             |   3 -
 flows/triggers/base.go                        |  34 ++-
 flows/triggers/base_test.go                   |   2 +-
 flows/triggers/flow_action.go                 |   5 +
 ...iggerMarshaling_channel_incoming_call.snap |   7 -
 ...TestTriggerMarshaling_flow_action_ivr.snap |   7 -
 .../TestTriggerMarshaling_manual_ivr.snap     |   7 -
 .../testdata/TestTriggerMarshaling_msg.snap   |   7 -
 flows/triggers/testdata/campaign.json         |   4 +-
 flows/triggers/testdata/channel.json          |   4 +-
 flows/triggers/testdata/flow_action.json      |  16 +-
 flows/triggers/testdata/manual.json           |   4 +-
 flows/triggers/testdata/msg.json              |   4 +-
 flows/triggers/testdata/optin.json            |   4 +-
 test/session.go                               |   2 +-
 test/testdata/runner/ivr_dial.busy.json       |  16 +-
 .../runner/ivr_dial.invalid_phone.json        |   9 +-
 test/testdata/runner/no_contact.json          |  87 --------
 test/testdata/runner/no_contact.test.json     | 205 ------------------
 41 files changed, 61 insertions(+), 754 deletions(-)
 delete mode 100644 test/testdata/runner/no_contact.json
 delete mode 100644 test/testdata/runner/no_contact.test.json

diff --git a/flows/actions/add_contact_groups.go b/flows/actions/add_contact_groups.go
index 9eada8704..f6e46096a 100644
--- a/flows/actions/add_contact_groups.go
+++ b/flows/actions/add_contact_groups.go
@@ -3,7 +3,6 @@ package actions
 import (
 	"github.com/nyaruka/goflow/assets"
 	"github.com/nyaruka/goflow/flows"
-	"github.com/nyaruka/goflow/flows/events"
 	"github.com/nyaruka/goflow/flows/modifiers"
 )
 
@@ -44,12 +43,6 @@ func NewAddContactGroups(uuid flows.ActionUUID, groups []*assets.GroupReference)
 
 // Execute adds our contact to the specified groups
 func (a *AddContactGroupsAction) Execute(run flows.Run, step flows.Step, logModifier flows.ModifierCallback, logEvent flows.EventCallback) error {
-	contact := run.Contact()
-	if contact == nil {
-		logEvent(events.NewErrorf("can't execute action in session without a contact"))
-		return nil
-	}
-
 	groups := resolveGroups(run, a.Groups, logEvent)
 
 	a.applyModifier(run, modifiers.NewGroups(groups, modifiers.GroupsAdd), logModifier, logEvent)
diff --git a/flows/actions/add_contact_urn.go b/flows/actions/add_contact_urn.go
index b9238de2b..12ee96060 100644
--- a/flows/actions/add_contact_urn.go
+++ b/flows/actions/add_contact_urn.go
@@ -47,13 +47,6 @@ func NewAddContactURN(uuid flows.ActionUUID, scheme string, path string) *AddCon
 
 // Execute runs the labeling action
 func (a *AddContactURNAction) Execute(run flows.Run, step flows.Step, logModifier flows.ModifierCallback, logEvent flows.EventCallback) error {
-	// only generate event if run has a contact
-	contact := run.Contact()
-	if contact == nil {
-		logEvent(events.NewErrorf("can't execute action in session without a contact"))
-		return nil
-	}
-
 	evaluatedPath, err := run.EvaluateTemplate(a.Path)
 
 	// if we received an error, log it although it might just be a non-expression like foo@bar.com
diff --git a/flows/actions/base_test.go b/flows/actions/base_test.go
index 319d8d3d0..2609dfd6e 100644
--- a/flows/actions/base_test.go
+++ b/flows/actions/base_test.go
@@ -83,7 +83,6 @@ func testActionType(t *testing.T, assetsJSON json.RawMessage, typeName string) {
 		Description  string               `json:"description"`
 		HTTPMocks    *httpx.MockRequestor `json:"http_mocks,omitempty"`
 		SMTPError    string               `json:"smtp_error,omitempty"`
-		NoContact    bool                 `json:"no_contact,omitempty"`
 		Contact      json.RawMessage      `json:"contact,omitempty"`
 		HasTicket    bool                 `json:"has_ticket,omitempty"`
 		NoInput      bool                 `json:"no_input,omitempty"`
@@ -165,26 +164,23 @@ func testActionType(t *testing.T, assetsJSON json.RawMessage, typeName string) {
 		}
 
 		// optionally load our contact
-		var contact *flows.Contact
-		if !tc.NoContact {
-			contactJSON := defaultContactJSON
-			if tc.Contact != nil {
-				contactJSON = tc.Contact
-			}
+		contactJSON := defaultContactJSON
+		if tc.Contact != nil {
+			contactJSON = tc.Contact
+		}
 
-			contact, err = flows.ReadContact(sa, contactJSON, assets.PanicOnMissing)
-			require.NoError(t, err)
+		contact, err := flows.ReadContact(sa, contactJSON, assets.PanicOnMissing)
+		require.NoError(t, err)
 
-			if tc.HasTicket {
-				ticketer := sa.Ticketers().Get("d605bb96-258d-4097-ad0a-080937db2212")
-				topic := sa.Topics().Get("0d9a2c56-6fc2-4f27-93c5-a6322e26b740")
-				contact.SetTicket(flows.NewTicket("7f44b065-ec28-4d7a-bbb4-0bda3b75b19d", ticketer, topic, "Help", "", nil))
-			}
+		if tc.HasTicket {
+			ticketer := sa.Ticketers().Get("d605bb96-258d-4097-ad0a-080937db2212")
+			topic := sa.Topics().Get("0d9a2c56-6fc2-4f27-93c5-a6322e26b740")
+			contact.SetTicket(flows.NewTicket("7f44b065-ec28-4d7a-bbb4-0bda3b75b19d", ticketer, topic, "Help", "", nil))
+		}
 
-			// and switch their language
-			if tc.Localization != nil {
-				contact.SetLanguage(i18n.Language("spa"))
-			}
+		// and switch their language
+		if tc.Localization != nil {
+			contact.SetLanguage(i18n.Language("spa"))
 		}
 
 		envBuilder := envs.NewBuilder().
diff --git a/flows/actions/remove_contact_groups.go b/flows/actions/remove_contact_groups.go
index 94e604ec2..9ed50b787 100644
--- a/flows/actions/remove_contact_groups.go
+++ b/flows/actions/remove_contact_groups.go
@@ -3,7 +3,6 @@ package actions
 import (
 	"github.com/nyaruka/goflow/assets"
 	"github.com/nyaruka/goflow/flows"
-	"github.com/nyaruka/goflow/flows/events"
 	"github.com/nyaruka/goflow/flows/modifiers"
 
 	"github.com/pkg/errors"
@@ -57,12 +56,6 @@ func (a *RemoveContactGroupsAction) Validate() error {
 
 // Execute runs the action
 func (a *RemoveContactGroupsAction) Execute(run flows.Run, step flows.Step, logModifier flows.ModifierCallback, logEvent flows.EventCallback) error {
-	contact := run.Contact()
-	if contact == nil {
-		logEvent(events.NewErrorf("can't execute action in session without a contact"))
-		return nil
-	}
-
 	var groups []*flows.Group
 
 	if a.AllGroups {
diff --git a/flows/actions/send_msg.go b/flows/actions/send_msg.go
index 07b05bf0e..3a3a09a21 100644
--- a/flows/actions/send_msg.go
+++ b/flows/actions/send_msg.go
@@ -75,11 +75,6 @@ func NewSendMsg(uuid flows.ActionUUID, text string, attachments []string, quickR
 
 // Execute runs this action
 func (a *SendMsgAction) Execute(run flows.Run, step flows.Step, logModifier flows.ModifierCallback, logEvent flows.EventCallback) error {
-	if run.Contact() == nil {
-		logEvent(events.NewErrorf("can't execute action in session without a contact"))
-		return nil
-	}
-
 	// a message to a non-active contact is unsendable but can still be created
 	unsendableReason := flows.NilUnsendableReason
 	if run.Contact().Status() != flows.ContactStatusActive {
diff --git a/flows/actions/set_contact_channel.go b/flows/actions/set_contact_channel.go
index e103f9012..b243f29e9 100644
--- a/flows/actions/set_contact_channel.go
+++ b/flows/actions/set_contact_channel.go
@@ -43,12 +43,6 @@ func NewSetContactChannel(uuid flows.ActionUUID, channel *assets.ChannelReferenc
 
 // Execute runs our action
 func (a *SetContactChannelAction) Execute(run flows.Run, step flows.Step, logModifier flows.ModifierCallback, logEvent flows.EventCallback) error {
-	contact := run.Contact()
-	if contact == nil {
-		logEvent(events.NewErrorf("can't execute action in session without a contact"))
-		return nil
-	}
-
 	var channel *flows.Channel
 	if a.Channel != nil {
 		channel = run.Session().Assets().Channels().Get(a.Channel.UUID)
diff --git a/flows/actions/set_contact_field.go b/flows/actions/set_contact_field.go
index 7f679c1db..c3d48b41e 100644
--- a/flows/actions/set_contact_field.go
+++ b/flows/actions/set_contact_field.go
@@ -47,11 +47,6 @@ func NewSetContactField(uuid flows.ActionUUID, field *assets.FieldReference, val
 
 // Execute runs this action
 func (a *SetContactFieldAction) Execute(run flows.Run, step flows.Step, logModifier flows.ModifierCallback, logEvent flows.EventCallback) error {
-	if run.Contact() == nil {
-		logEvent(events.NewErrorf("can't execute action in session without a contact"))
-		return nil
-	}
-
 	value, err := run.EvaluateTemplate(a.Value)
 	value = strings.TrimSpace(value)
 
diff --git a/flows/actions/set_contact_language.go b/flows/actions/set_contact_language.go
index 87e6bc716..e9a8aa044 100644
--- a/flows/actions/set_contact_language.go
+++ b/flows/actions/set_contact_language.go
@@ -44,11 +44,6 @@ func NewSetContactLanguage(uuid flows.ActionUUID, language string) *SetContactLa
 
 // Execute runs this action
 func (a *SetContactLanguageAction) Execute(run flows.Run, step flows.Step, logModifier flows.ModifierCallback, logEvent flows.EventCallback) error {
-	if run.Contact() == nil {
-		logEvent(events.NewErrorf("can't execute action in session without a contact"))
-		return nil
-	}
-
 	language, err := run.EvaluateTemplate(a.Language)
 	language = strings.TrimSpace(language)
 
diff --git a/flows/actions/set_contact_name.go b/flows/actions/set_contact_name.go
index f761534eb..620856caa 100644
--- a/flows/actions/set_contact_name.go
+++ b/flows/actions/set_contact_name.go
@@ -43,11 +43,6 @@ func NewSetContactName(uuid flows.ActionUUID, name string) *SetContactNameAction
 
 // Execute runs this action
 func (a *SetContactNameAction) Execute(run flows.Run, step flows.Step, logModifier flows.ModifierCallback, logEvent flows.EventCallback) error {
-	if run.Contact() == nil {
-		logEvent(events.NewErrorf("can't execute action in session without a contact"))
-		return nil
-	}
-
 	name, err := run.EvaluateTemplate(a.Name)
 	name = strings.TrimSpace(name)
 
diff --git a/flows/actions/set_contact_status.go b/flows/actions/set_contact_status.go
index a087f26e8..d53714a94 100644
--- a/flows/actions/set_contact_status.go
+++ b/flows/actions/set_contact_status.go
@@ -2,7 +2,6 @@ package actions
 
 import (
 	"github.com/nyaruka/goflow/flows"
-	"github.com/nyaruka/goflow/flows/events"
 	"github.com/nyaruka/goflow/flows/modifiers"
 )
 
@@ -40,11 +39,6 @@ func NewSetContactStatus(uuid flows.ActionUUID, status flows.ContactStatus) *Set
 
 // Execute runs this action
 func (a *SetContactStatusAction) Execute(run flows.Run, step flows.Step, logModifier flows.ModifierCallback, logEvent flows.EventCallback) error {
-	if run.Contact() == nil {
-		logEvent(events.NewErrorf("can't execute action in session without a contact"))
-		return nil
-	}
-
 	a.applyModifier(run, modifiers.NewStatus(a.Status), logModifier, logEvent)
 	return nil
 }
diff --git a/flows/actions/set_contact_timezone.go b/flows/actions/set_contact_timezone.go
index d00d96c67..7be57c10a 100644
--- a/flows/actions/set_contact_timezone.go
+++ b/flows/actions/set_contact_timezone.go
@@ -44,11 +44,6 @@ func NewSetContactTimezone(uuid flows.ActionUUID, timezone string) *SetContactTi
 
 // Execute runs this action
 func (a *SetContactTimezoneAction) Execute(run flows.Run, step flows.Step, logModifier flows.ModifierCallback, logEvent flows.EventCallback) error {
-	if run.Contact() == nil {
-		logEvent(events.NewErrorf("can't execute action in session without a contact"))
-		return nil
-	}
-
 	timezone, err := run.EvaluateTemplate(a.Timezone)
 	timezone = strings.TrimSpace(timezone)
 
diff --git a/flows/actions/testdata/add_contact_groups.json b/flows/actions/testdata/add_contact_groups.json
index dde9676d4..95c9e2f1c 100644
--- a/flows/actions/testdata/add_contact_groups.json
+++ b/flows/actions/testdata/add_contact_groups.json
@@ -1,26 +1,4 @@
 [
-    {
-        "description": "Error event if session has no contact",
-        "no_contact": true,
-        "action": {
-            "type": "add_contact_groups",
-            "uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
-            "groups": [
-                {
-                    "uuid": "b7cf0d83-f1c9-411c-96fd-c511a4cfa86d",
-                    "name": "Testers"
-                }
-            ]
-        },
-        "events": [
-            {
-                "type": "error",
-                "created_on": "2018-10-18T14:20:30.000123456Z",
-                "step_uuid": "59d74b86-3e2f-4a93-aece-b05d2fdcde0c",
-                "text": "can't execute action in session without a contact"
-            }
-        ]
-    },
     {
         "description": "Error event and NOOP for missing group",
         "action": {
diff --git a/flows/actions/testdata/add_contact_urn.json b/flows/actions/testdata/add_contact_urn.json
index 91ecd607f..f5ae3a509 100644
--- a/flows/actions/testdata/add_contact_urn.json
+++ b/flows/actions/testdata/add_contact_urn.json
@@ -1,32 +1,4 @@
 [
-    {
-        "description": "Error event if session has no contact",
-        "no_contact": true,
-        "action": {
-            "type": "add_contact_urn",
-            "uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
-            "scheme": "tel",
-            "path": "+1234567890"
-        },
-        "events": [
-            {
-                "type": "error",
-                "created_on": "2018-10-18T14:20:30.000123456Z",
-                "step_uuid": "59d74b86-3e2f-4a93-aece-b05d2fdcde0c",
-                "text": "can't execute action in session without a contact"
-            }
-        ],
-        "templates": [
-            "+1234567890"
-        ],
-        "inspection": {
-            "dependencies": [],
-            "issues": [],
-            "results": [],
-            "waiting_exits": [],
-            "parent_refs": []
-        }
-    },
     {
         "description": "Error event if path evaluates to empty",
         "action": {
diff --git a/flows/actions/testdata/remove_contact_groups.json b/flows/actions/testdata/remove_contact_groups.json
index 324d0e588..2833b9455 100644
--- a/flows/actions/testdata/remove_contact_groups.json
+++ b/flows/actions/testdata/remove_contact_groups.json
@@ -14,28 +14,6 @@
         },
         "read_error": "can't specify specific groups when all_groups=true"
     },
-    {
-        "description": "Error event if session has no contact",
-        "no_contact": true,
-        "action": {
-            "type": "remove_contact_groups",
-            "uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
-            "groups": [
-                {
-                    "uuid": "b7cf0d83-f1c9-411c-96fd-c511a4cfa86d",
-                    "name": "Testers"
-                }
-            ]
-        },
-        "events": [
-            {
-                "type": "error",
-                "created_on": "2018-10-18T14:20:30.000123456Z",
-                "step_uuid": "59d74b86-3e2f-4a93-aece-b05d2fdcde0c",
-                "text": "can't execute action in session without a contact"
-            }
-        ]
-    },
     {
         "description": "Error event if a group is query based",
         "action": {
diff --git a/flows/actions/testdata/send_msg.json b/flows/actions/testdata/send_msg.json
index 6a71f96a5..ed809f30f 100644
--- a/flows/actions/testdata/send_msg.json
+++ b/flows/actions/testdata/send_msg.json
@@ -21,50 +21,6 @@
         },
         "read_error": "field 'topic' is not a valid message topic"
     },
-    {
-        "description": "Error event if session has no contact",
-        "no_contact": true,
-        "action": {
-            "type": "send_msg",
-            "uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
-            "text": "Hi there",
-            "attachments": [
-                "http://example.com/red.jpg"
-            ],
-            "quick_replies": [
-                "Red",
-                "Blue"
-            ],
-            "all_urns": true
-        },
-        "events": [
-            {
-                "type": "error",
-                "created_on": "2018-10-18T14:20:30.000123456Z",
-                "step_uuid": "59d74b86-3e2f-4a93-aece-b05d2fdcde0c",
-                "text": "can't execute action in session without a contact"
-            }
-        ],
-        "templates": [
-            "Hi there",
-            "http://example.com/red.jpg",
-            "Red",
-            "Blue"
-        ],
-        "localizables": [
-            "Hi there",
-            "http://example.com/red.jpg",
-            "Red",
-            "Blue"
-        ],
-        "inspection": {
-            "dependencies": [],
-            "issues": [],
-            "results": [],
-            "waiting_exits": [],
-            "parent_refs": []
-        }
-    },
     {
         "description": "Error events if msg text, attachments and quick replies have expression errors",
         "action": {
diff --git a/flows/actions/testdata/set_contact_channel.json b/flows/actions/testdata/set_contact_channel.json
index e7df870d0..5733b5bbc 100644
--- a/flows/actions/testdata/set_contact_channel.json
+++ b/flows/actions/testdata/set_contact_channel.json
@@ -1,24 +1,4 @@
 [
-    {
-        "description": "Error event if session has no contact",
-        "no_contact": true,
-        "action": {
-            "type": "set_contact_channel",
-            "uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
-            "channel": {
-                "uuid": "57f1078f-88aa-46f4-a59a-948a5739c03d",
-                "name": "My Android Phone"
-            }
-        },
-        "events": [
-            {
-                "type": "error",
-                "created_on": "2018-10-18T14:20:30.000123456Z",
-                "step_uuid": "59d74b86-3e2f-4a93-aece-b05d2fdcde0c",
-                "text": "can't execute action in session without a contact"
-            }
-        ]
-    },
     {
         "description": "NOOP if channel doesn't change",
         "action": {
diff --git a/flows/actions/testdata/set_contact_field.json b/flows/actions/testdata/set_contact_field.json
index 1e06a23ea..5e7551a17 100644
--- a/flows/actions/testdata/set_contact_field.json
+++ b/flows/actions/testdata/set_contact_field.json
@@ -1,41 +1,4 @@
 [
-    {
-        "description": "Error event if session has no contact",
-        "no_contact": true,
-        "action": {
-            "type": "set_contact_field",
-            "uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
-            "field": {
-                "key": "age",
-                "name": "Age"
-            },
-            "value": "30"
-        },
-        "events": [
-            {
-                "type": "error",
-                "created_on": "2018-10-18T14:20:30.000123456Z",
-                "step_uuid": "59d74b86-3e2f-4a93-aece-b05d2fdcde0c",
-                "text": "can't execute action in session without a contact"
-            }
-        ],
-        "templates": [
-            "30"
-        ],
-        "inspection": {
-            "dependencies": [
-                {
-                    "key": "age",
-                    "name": "Age",
-                    "type": "field"
-                }
-            ],
-            "issues": [],
-            "results": [],
-            "waiting_exits": [],
-            "parent_refs": []
-        }
-    },
     {
         "description": "Error event and action skipped if value contains expression error",
         "action": {
diff --git a/flows/actions/testdata/set_contact_language.json b/flows/actions/testdata/set_contact_language.json
index c7dc1cda3..11b92306b 100644
--- a/flows/actions/testdata/set_contact_language.json
+++ b/flows/actions/testdata/set_contact_language.json
@@ -1,21 +1,4 @@
 [
-    {
-        "description": "Error event if session has no contact",
-        "no_contact": true,
-        "action": {
-            "type": "set_contact_language",
-            "uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
-            "language": "eng"
-        },
-        "events": [
-            {
-                "type": "error",
-                "created_on": "2018-10-18T14:20:30.000123456Z",
-                "step_uuid": "59d74b86-3e2f-4a93-aece-b05d2fdcde0c",
-                "text": "can't execute action in session without a contact"
-            }
-        ]
-    },
     {
         "description": "Error event and action skipped if language contains expression error",
         "action": {
diff --git a/flows/actions/testdata/set_contact_name.json b/flows/actions/testdata/set_contact_name.json
index 49b104833..025bda6a7 100644
--- a/flows/actions/testdata/set_contact_name.json
+++ b/flows/actions/testdata/set_contact_name.json
@@ -1,31 +1,4 @@
 [
-    {
-        "description": "Error event if session has no contact",
-        "no_contact": true,
-        "action": {
-            "type": "set_contact_name",
-            "uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
-            "name": "John"
-        },
-        "events": [
-            {
-                "type": "error",
-                "created_on": "2018-10-18T14:20:30.000123456Z",
-                "step_uuid": "59d74b86-3e2f-4a93-aece-b05d2fdcde0c",
-                "text": "can't execute action in session without a contact"
-            }
-        ],
-        "templates": [
-            "John"
-        ],
-        "inspection": {
-            "dependencies": [],
-            "issues": [],
-            "results": [],
-            "waiting_exits": [],
-            "parent_refs": []
-        }
-    },
     {
         "description": "Error event and action skipped if name contains expression error",
         "action": {
diff --git a/flows/actions/testdata/set_contact_status.json b/flows/actions/testdata/set_contact_status.json
index ec59968ea..cb023cfbf 100644
--- a/flows/actions/testdata/set_contact_status.json
+++ b/flows/actions/testdata/set_contact_status.json
@@ -8,23 +8,6 @@
         },
         "read_error": "field 'status' is not a valid contact status"
     },
-    {
-        "description": "Error event if session has no contact",
-        "no_contact": true,
-        "action": {
-            "type": "set_contact_status",
-            "uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
-            "status": "blocked"
-        },
-        "events": [
-            {
-                "type": "error",
-                "created_on": "2018-10-18T14:20:30.000123456Z",
-                "step_uuid": "59d74b86-3e2f-4a93-aece-b05d2fdcde0c",
-                "text": "can't execute action in session without a contact"
-            }
-        ]
-    },
     {
         "description": "NOOP if status doesn't change",
         "action": {
diff --git a/flows/actions/testdata/set_contact_timezone.json b/flows/actions/testdata/set_contact_timezone.json
index 55f5871fa..a3afa3bc6 100644
--- a/flows/actions/testdata/set_contact_timezone.json
+++ b/flows/actions/testdata/set_contact_timezone.json
@@ -1,21 +1,4 @@
 [
-    {
-        "description": "Error event if session has no contact",
-        "no_contact": true,
-        "action": {
-            "type": "set_contact_timezone",
-            "uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
-            "timezone": "Africa/Kigali"
-        },
-        "events": [
-            {
-                "type": "error",
-                "created_on": "2018-10-18T14:20:30.000123456Z",
-                "step_uuid": "59d74b86-3e2f-4a93-aece-b05d2fdcde0c",
-                "text": "can't execute action in session without a contact"
-            }
-        ]
-    },
     {
         "description": "Error event and action skipped if timezone contains expression error",
         "action": {
diff --git a/flows/actions/testdata/transfer_airtime.json b/flows/actions/testdata/transfer_airtime.json
index 368e442d3..2c74b89b2 100644
--- a/flows/actions/testdata/transfer_airtime.json
+++ b/flows/actions/testdata/transfer_airtime.json
@@ -1,51 +1,4 @@
 [
-    {
-        "description": "Error and failed transfer if no contact",
-        "no_contact": true,
-        "action": {
-            "type": "transfer_airtime",
-            "uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
-            "amounts": {
-                "USD": 0.5
-            },
-            "result_name": "Reward Transfer"
-        },
-        "events": [
-            {
-                "type": "error",
-                "created_on": "2018-10-18T14:20:30.000123456Z",
-                "step_uuid": "59d74b86-3e2f-4a93-aece-b05d2fdcde0c",
-                "text": "can't execute action in session without a contact"
-            },
-            {
-                "type": "run_result_changed",
-                "created_on": "2018-10-18T14:20:30.000123456Z",
-                "step_uuid": "59d74b86-3e2f-4a93-aece-b05d2fdcde0c",
-                "name": "Reward Transfer",
-                "value": "0",
-                "category": "Failure"
-            }
-        ],
-        "inspection": {
-            "dependencies": [],
-            "issues": [],
-            "results": [
-                {
-                    "key": "reward_transfer",
-                    "name": "Reward Transfer",
-                    "categories": [
-                        "Success",
-                        "Failure"
-                    ],
-                    "node_uuids": [
-                        "72a1f5df-49f9-45df-94c9-d86f7ea064e5"
-                    ]
-                }
-            ],
-            "waiting_exits": [],
-            "parent_refs": []
-        }
-    },
     {
         "description": "Error and failed transfer if contact has no tel urn",
         "contact": {
diff --git a/flows/actions/transfer_airtime.go b/flows/actions/transfer_airtime.go
index 5e9f3fc24..26e4bc996 100644
--- a/flows/actions/transfer_airtime.go
+++ b/flows/actions/transfer_airtime.go
@@ -64,9 +64,6 @@ func (a *TransferAirtimeAction) Execute(run flows.Run, step flows.Step, logModif
 func (a *TransferAirtimeAction) transfer(run flows.Run, step flows.Step, logEvent flows.EventCallback) (*flows.AirtimeTransfer, error) {
 	// fail if we don't have a contact
 	contact := run.Contact()
-	if contact == nil {
-		return nil, errors.New("can't execute action in session without a contact")
-	}
 
 	// fail if the contact doesn't have a tel URN
 	telURNs := contact.URNs().WithScheme(urns.TelScheme)
diff --git a/flows/triggers/base.go b/flows/triggers/base.go
index ec9d0fb58..709962a24 100644
--- a/flows/triggers/base.go
+++ b/flows/triggers/base.go
@@ -176,16 +176,15 @@ func NewBuilder(env envs.Environment, flow *assets.FlowReference, contact *flows
 //------------------------------------------------------------------------------------------
 
 type baseTriggerEnvelope struct {
-	Type        string                `json:"type" validate:"required"`
+	Type        string                `json:"type"                  validate:"required"`
 	Environment json.RawMessage       `json:"environment,omitempty"`
-	Flow        *assets.FlowReference `json:"flow" validate:"required"`
-	Contact     json.RawMessage       `json:"contact,omitempty"`
+	Flow        *assets.FlowReference `json:"flow"                  validate:"required"`
+	Contact     json.RawMessage       `json:"contact"               validate:"required"`
 	Call        *flows.Call           `json:"call,omitempty"`
-	Connection  *flows.Call           `json:"connection,omitempty"` // backwards compatibility
 	Batch       bool                  `json:"batch,omitempty"`
 	Params      json.RawMessage       `json:"params,omitempty"`
 	History     *flows.SessionHistory `json:"history,omitempty"`
-	TriggeredOn time.Time             `json:"triggered_on" validate:"required"`
+	TriggeredOn time.Time             `json:"triggered_on"          validate:"required"`
 }
 
 // ReadTrigger reads a trigger from the given JSON
@@ -208,23 +207,20 @@ func (t *baseTrigger) unmarshal(sessionAssets flows.SessionAssets, e *baseTrigge
 	t.type_ = e.Type
 	t.flow = e.Flow
 	t.call = e.Call
-	if e.Connection != nil {
-		t.call = e.Connection
-	}
 	t.batch = e.Batch
 	t.history = e.History
 	t.triggeredOn = e.TriggeredOn
 
+	if t.contact, err = flows.ReadContact(sessionAssets, e.Contact, missing); err != nil {
+		return errors.Wrap(err, "unable to read contact")
+	}
+
 	if e.Environment != nil {
 		if t.environment, err = envs.ReadEnvironment(e.Environment); err != nil {
 			return errors.Wrap(err, "unable to read environment")
 		}
 	}
-	if e.Contact != nil {
-		if t.contact, err = flows.ReadContact(sessionAssets, e.Contact, missing); err != nil {
-			return errors.Wrap(err, "unable to read contact")
-		}
-	}
+
 	if e.Params != nil {
 		if t.params, err = types.ReadXObject(e.Params); err != nil {
 			return errors.Wrap(err, "unable to read params")
@@ -239,23 +235,21 @@ func (t *baseTrigger) marshal(e *baseTriggerEnvelope) error {
 	e.Type = t.type_
 	e.Flow = t.flow
 	e.Call = t.call
-	e.Connection = t.call
 	e.Batch = t.batch
 	e.History = t.history
 	e.TriggeredOn = t.triggeredOn
 
+	e.Contact, err = jsonx.Marshal(t.contact)
+	if err != nil {
+		return err
+	}
+
 	if t.environment != nil {
 		e.Environment, err = jsonx.Marshal(t.environment)
 		if err != nil {
 			return err
 		}
 	}
-	if t.contact != nil {
-		e.Contact, err = jsonx.Marshal(t.contact)
-		if err != nil {
-			return err
-		}
-	}
 	if t.params != nil {
 		e.Params, err = jsonx.Marshal(t.params)
 		if err != nil {
diff --git a/flows/triggers/base_test.go b/flows/triggers/base_test.go
index 1f40d9e67..4ed2be066 100644
--- a/flows/triggers/base_test.go
+++ b/flows/triggers/base_test.go
@@ -360,7 +360,7 @@ func TestReadTrigger(t *testing.T) {
 				"tel:+12065551212"
 			]
 		},
-		"connection": {
+		"call": {
 			"channel": {
 				"uuid": "3a05eaf5-cb1b-4246-bef1-f277419c83a7",
 				"name": "Nexmo"
diff --git a/flows/triggers/flow_action.go b/flows/triggers/flow_action.go
index 9f8a772b1..3a5bfc70b 100644
--- a/flows/triggers/flow_action.go
+++ b/flows/triggers/flow_action.go
@@ -23,6 +23,11 @@ const TypeFlowAction string = "flow_action"
 //	{
 //	  "type": "flow_action",
 //	  "flow": {"uuid": "b7cf0d83-f1c9-411c-96fd-c511a4cfa86d", "name": "Collect Age"},
+//	  "contact": {
+//	    "uuid": "9f7ede93-4b16-4692-80ad-b7dc54a1cd81",
+//	    "name": "Bob",
+//	    "created_on": "2018-01-01T12:00:00.000000Z"
+//	  },
 //	  "history": {
 //	    "parent_uuid": "a5b25fb0-75fd-4898-a34f-5ff14fc19078",
 //	    "ancestors": 3,
diff --git a/flows/triggers/testdata/TestTriggerMarshaling_channel_incoming_call.snap b/flows/triggers/testdata/TestTriggerMarshaling_channel_incoming_call.snap
index e0e69d11e..d4a85a477 100644
--- a/flows/triggers/testdata/TestTriggerMarshaling_channel_incoming_call.snap
+++ b/flows/triggers/testdata/TestTriggerMarshaling_channel_incoming_call.snap
@@ -32,13 +32,6 @@
         },
         "urn": "tel:+12065551212"
     },
-    "connection": {
-        "channel": {
-            "uuid": "3a05eaf5-cb1b-4246-bef1-f277419c83a7",
-            "name": "Nexmo"
-        },
-        "urn": "tel:+12065551212"
-    },
     "triggered_on": "2018-10-20T09:49:31.23456789Z",
     "event": {
         "type": "incoming_call",
diff --git a/flows/triggers/testdata/TestTriggerMarshaling_flow_action_ivr.snap b/flows/triggers/testdata/TestTriggerMarshaling_flow_action_ivr.snap
index 618fa2740..2d3e9d643 100644
--- a/flows/triggers/testdata/TestTriggerMarshaling_flow_action_ivr.snap
+++ b/flows/triggers/testdata/TestTriggerMarshaling_flow_action_ivr.snap
@@ -32,13 +32,6 @@
         },
         "urn": "tel:+12065551212"
     },
-    "connection": {
-        "channel": {
-            "uuid": "3a05eaf5-cb1b-4246-bef1-f277419c83a7",
-            "name": "Nexmo"
-        },
-        "urn": "tel:+12065551212"
-    },
     "batch": true,
     "history": {
         "parent_uuid": "cdf7ed27-5ad5-4028-b664-880fc7581c77",
diff --git a/flows/triggers/testdata/TestTriggerMarshaling_manual_ivr.snap b/flows/triggers/testdata/TestTriggerMarshaling_manual_ivr.snap
index 2bcba6e2a..c4474d1c7 100644
--- a/flows/triggers/testdata/TestTriggerMarshaling_manual_ivr.snap
+++ b/flows/triggers/testdata/TestTriggerMarshaling_manual_ivr.snap
@@ -32,13 +32,6 @@
         },
         "urn": "tel:+12065551212"
     },
-    "connection": {
-        "channel": {
-            "uuid": "3a05eaf5-cb1b-4246-bef1-f277419c83a7",
-            "name": "Nexmo"
-        },
-        "urn": "tel:+12065551212"
-    },
     "batch": true,
     "params": {
         "foo": "bar"
diff --git a/flows/triggers/testdata/TestTriggerMarshaling_msg.snap b/flows/triggers/testdata/TestTriggerMarshaling_msg.snap
index 02a4e3600..855a91d39 100644
--- a/flows/triggers/testdata/TestTriggerMarshaling_msg.snap
+++ b/flows/triggers/testdata/TestTriggerMarshaling_msg.snap
@@ -32,13 +32,6 @@
         },
         "urn": "tel:+12065551212"
     },
-    "connection": {
-        "channel": {
-            "uuid": "3a05eaf5-cb1b-4246-bef1-f277419c83a7",
-            "name": "Nexmo"
-        },
-        "urn": "tel:+12065551212"
-    },
     "triggered_on": "2018-10-20T09:49:31.23456789Z",
     "msg": {
         "uuid": "c8005ee3-4628-4d76-be66-906352cb1935",
diff --git a/flows/triggers/testdata/campaign.json b/flows/triggers/testdata/campaign.json
index 5efd2c8a7..bc13e6d68 100644
--- a/flows/triggers/testdata/campaign.json
+++ b/flows/triggers/testdata/campaign.json
@@ -1,6 +1,6 @@
 [
     {
-        "description": "flow is required",
+        "description": "flow and contact are required",
         "trigger": {
             "type": "campaign",
             "event": {
@@ -12,7 +12,7 @@
             },
             "triggered_on": "2000-01-01T00:00:00Z"
         },
-        "read_error": "field 'flow' is required"
+        "read_error": "field 'flow' is required, field 'contact' is required"
     },
     {
         "description": "event is required",
diff --git a/flows/triggers/testdata/channel.json b/flows/triggers/testdata/channel.json
index 174ec6033..da41ce616 100644
--- a/flows/triggers/testdata/channel.json
+++ b/flows/triggers/testdata/channel.json
@@ -1,6 +1,6 @@
 [
     {
-        "description": "flow is required",
+        "description": "flow and contact are required",
         "trigger": {
             "type": "channel",
             "event": {
@@ -12,7 +12,7 @@
             },
             "triggered_on": "2000-01-01T00:00:00Z"
         },
-        "read_error": "field 'flow' is required"
+        "read_error": "field 'flow' is required, field 'contact' is required"
     },
     {
         "description": "event is required",
diff --git a/flows/triggers/testdata/flow_action.json b/flows/triggers/testdata/flow_action.json
index b4af34b7f..5751e35a6 100644
--- a/flows/triggers/testdata/flow_action.json
+++ b/flows/triggers/testdata/flow_action.json
@@ -1,6 +1,6 @@
 [
     {
-        "description": "flow is required",
+        "description": "flow and contact are required",
         "trigger": {
             "type": "flow_action",
             "run_summary": {
@@ -31,7 +31,7 @@
             },
             "triggered_on": "2000-01-01T00:00:00Z"
         },
-        "read_error": "field 'flow' is required"
+        "read_error": "field 'flow' is required, field 'contact' is required"
     },
     {
         "description": "run summary is required",
@@ -41,6 +41,12 @@
                 "uuid": "bead76f5-dac4-4c9d-996c-c62b326e8c0a",
                 "name": "Trigger Tester"
             },
+            "contact": {
+                "uuid": "9f7ede93-4b16-4692-80ad-b7dc54a1cd81",
+                "name": "Bob",
+                "status": "active",
+                "created_on": "2018-01-01T12:00:00Z"
+            },
             "triggered_on": "2000-01-01T00:00:00Z"
         },
         "read_error": "field 'run_summary' is required"
@@ -53,6 +59,12 @@
                 "uuid": "bead76f5-dac4-4c9d-996c-c62b326e8c0a",
                 "name": "Trigger Tester"
             },
+            "contact": {
+                "uuid": "9f7ede93-4b16-4692-80ad-b7dc54a1cd81",
+                "name": "Bob",
+                "status": "active",
+                "created_on": "2018-01-01T12:00:00Z"
+            },
             "triggered_on": "2000-01-01T00:00:00Z",
             "run_summary": {
                 "uuid": "b7cf0d83-f1c9-411c-96fd-c511a4cfa86d",
diff --git a/flows/triggers/testdata/manual.json b/flows/triggers/testdata/manual.json
index 22f777962..049d7c386 100644
--- a/flows/triggers/testdata/manual.json
+++ b/flows/triggers/testdata/manual.json
@@ -1,11 +1,11 @@
 [
     {
-        "description": "flow is required",
+        "description": "flow and contact are required",
         "trigger": {
             "type": "manual",
             "triggered_on": "2000-01-01T00:00:00Z"
         },
-        "read_error": "field 'flow' is required"
+        "read_error": "field 'flow' is required, field 'contact' is required"
     },
     {
         "description": "params, user and origin are accessible in context",
diff --git a/flows/triggers/testdata/msg.json b/flows/triggers/testdata/msg.json
index 3ed6a1a4d..aea03491c 100644
--- a/flows/triggers/testdata/msg.json
+++ b/flows/triggers/testdata/msg.json
@@ -1,6 +1,6 @@
 [
     {
-        "description": "flow is required",
+        "description": "flow and contact are required",
         "trigger": {
             "type": "msg",
             "msg": {
@@ -17,7 +17,7 @@
             },
             "triggered_on": "2000-01-01T00:00:00Z"
         },
-        "read_error": "field 'flow' is required"
+        "read_error": "field 'flow' is required, field 'contact' is required"
     },
     {
         "description": "msg is required",
diff --git a/flows/triggers/testdata/optin.json b/flows/triggers/testdata/optin.json
index 68db0066b..6abcdd4c0 100644
--- a/flows/triggers/testdata/optin.json
+++ b/flows/triggers/testdata/optin.json
@@ -1,6 +1,6 @@
 [
     {
-        "description": "flow is required",
+        "description": "flow and contact are required",
         "trigger": {
             "type": "optin",
             "event": {
@@ -12,7 +12,7 @@
             },
             "triggered_on": "2000-01-01T00:00:00Z"
         },
-        "read_error": "field 'flow' is required"
+        "read_error": "field 'flow' is required, field 'contact' is required"
     },
     {
         "description": "event is required",
diff --git a/test/session.go b/test/session.go
index 3a8f557af..6a098b4f8 100644
--- a/test/session.go
+++ b/test/session.go
@@ -468,7 +468,7 @@ var voiceSessionTrigger = `{
         "type": "incoming_call",
         "channel": {"uuid": "fd47a886-451b-46fb-bcb6-242a4046c0c0", "name": "Nexmo"}
     },
-    "connection": {
+    "call": {
         "channel": {"uuid": "fd47a886-451b-46fb-bcb6-242a4046c0c0", "name": "Nexmo"},
         "urn": "tel:+12065551212"
     },
diff --git a/test/testdata/runner/ivr_dial.busy.json b/test/testdata/runner/ivr_dial.busy.json
index b48785a6f..fba76f5c6 100644
--- a/test/testdata/runner/ivr_dial.busy.json
+++ b/test/testdata/runner/ivr_dial.busy.json
@@ -88,13 +88,6 @@
                         },
                         "urn": "tel:+12065551212"
                     },
-                    "connection": {
-                        "channel": {
-                            "name": "Twilio",
-                            "uuid": "a78930fe-6a40-4aa8-99c3-e61b02f45ca1"
-                        },
-                        "urn": "tel:+12065551212"
-                    },
                     "contact": {
                         "created_on": "2018-01-01T12:00:00Z",
                         "fields": {
@@ -266,13 +259,6 @@
                         },
                         "urn": "tel:+12065551212"
                     },
-                    "connection": {
-                        "channel": {
-                            "name": "Twilio",
-                            "uuid": "a78930fe-6a40-4aa8-99c3-e61b02f45ca1"
-                        },
-                        "urn": "tel:+12065551212"
-                    },
                     "contact": {
                         "created_on": "2018-01-01T12:00:00Z",
                         "fields": {
@@ -330,7 +316,7 @@
         }
     ],
     "trigger": {
-        "connection": {
+        "call": {
             "channel": {
                 "name": "Twilio",
                 "uuid": "a78930fe-6a40-4aa8-99c3-e61b02f45ca1"
diff --git a/test/testdata/runner/ivr_dial.invalid_phone.json b/test/testdata/runner/ivr_dial.invalid_phone.json
index d058581bd..da35f3795 100644
--- a/test/testdata/runner/ivr_dial.invalid_phone.json
+++ b/test/testdata/runner/ivr_dial.invalid_phone.json
@@ -108,13 +108,6 @@
                         },
                         "urn": "tel:+12065551212"
                     },
-                    "connection": {
-                        "channel": {
-                            "name": "Twilio",
-                            "uuid": "a78930fe-6a40-4aa8-99c3-e61b02f45ca1"
-                        },
-                        "urn": "tel:+12065551212"
-                    },
                     "contact": {
                         "created_on": "2018-01-01T12:00:00Z",
                         "fields": {
@@ -163,7 +156,7 @@
     ],
     "resumes": null,
     "trigger": {
-        "connection": {
+        "call": {
             "channel": {
                 "name": "Twilio",
                 "uuid": "a78930fe-6a40-4aa8-99c3-e61b02f45ca1"
diff --git a/test/testdata/runner/no_contact.json b/test/testdata/runner/no_contact.json
deleted file mode 100644
index 3eeea2acb..000000000
--- a/test/testdata/runner/no_contact.json
+++ /dev/null
@@ -1,87 +0,0 @@
-{
-    "flows": [
-        {
-            "uuid": "8ca44c09-791d-453a-9799-a70dd3303306",
-            "name": "Registration Flow",
-            "spec_version": "13.0",
-            "language": "eng",
-            "type": "messaging",
-            "nodes": [
-                {
-                    "uuid": "a58be63b-907d-4a1a-856b-0bb5579d7507",
-                    "actions": [
-                        {
-                            "uuid": "ad154980-7bf7-4ab8-8728-545fd6378912",
-                            "type": "add_input_labels",
-                            "labels": [
-                                {
-                                    "uuid": "3f65d88a-95dc-4140-9451-943e94e06fea",
-                                    "name": "Spam"
-                                }
-                            ]
-                        },
-                        {
-                            "uuid": "c62f296f-f6dd-4a7c-ba11-8a289ca09d6f",
-                            "type": "add_contact_groups",
-                            "groups": [
-                                {
-                                    "uuid": "2aad21f6-30b7-42c5-bd7f-1b720c154817",
-                                    "name": "Survey Audience"
-                                },
-                                {
-                                    "name_match": "@(format_location(contact.fields.state)) State"
-                                }
-                            ]
-                        },
-                        {
-                            "uuid": "ca5138c1-2a26-44c6-a29c-6ef695bc67ee",
-                            "type": "add_contact_urn",
-                            "scheme": "twitter",
-                            "path": "@(replace(lower(contact.name), \" \", \"_\"))"
-                        }
-                    ],
-                    "exits": [
-                        {
-                            "uuid": "28039554-80a8-4a5d-9024-90ec1dacdd47"
-                        }
-                    ]
-                }
-            ]
-        }
-    ],
-    "groups": [
-        {
-            "uuid": "2aad21f6-30b7-42c5-bd7f-1b720c154817",
-            "name": "Survey Audience"
-        },
-        {
-            "uuid": "d7ff4872-9238-452f-9d38-2f558fea89e0",
-            "name": "Kigali City Members"
-        }
-    ],
-    "labels": [
-        {
-            "uuid": "3f65d88a-95dc-4140-9451-943e94e06fea",
-            "name": "Spam"
-        },
-        {
-            "uuid": "b017c07a-d35b-4da4-8917-3bf8bff80168",
-            "name": "Kigali City Messages"
-        }
-    ],
-    "channels": [
-        {
-            "uuid": "57f1078f-88aa-46f4-a59a-948a5739c03d",
-            "name": "Android Channel",
-            "address": "+17036975131",
-            "schemes": [
-                "tel"
-            ],
-            "roles": [
-                "send",
-                "receive"
-            ],
-            "country": "US"
-        }
-    ]
-}
\ No newline at end of file
diff --git a/test/testdata/runner/no_contact.test.json b/test/testdata/runner/no_contact.test.json
deleted file mode 100644
index 9c2f3838b..000000000
--- a/test/testdata/runner/no_contact.test.json
+++ /dev/null
@@ -1,205 +0,0 @@
-{
-    "outputs": [
-        {
-            "events": [
-                {
-                    "created_on": "2018-07-06T12:30:02.123456789Z",
-                    "msg": {
-                        "channel": {
-                            "name": "Nexmo",
-                            "uuid": "57f1078f-88aa-46f4-a59a-948a5739c03d"
-                        },
-                        "text": "Ryan Lewis",
-                        "urn": "tel:+12065551212",
-                        "uuid": "9bf91c2b-ce58-4cef-aacc-281e03f69ab5"
-                    },
-                    "step_uuid": "8720f157-ca1c-432f-9c0b-2014ddc77094",
-                    "type": "msg_received"
-                },
-                {
-                    "created_on": "2018-07-06T12:30:04.123456789Z",
-                    "input_uuid": "9bf91c2b-ce58-4cef-aacc-281e03f69ab5",
-                    "labels": [
-                        {
-                            "name": "Spam",
-                            "uuid": "3f65d88a-95dc-4140-9451-943e94e06fea"
-                        }
-                    ],
-                    "step_uuid": "8720f157-ca1c-432f-9c0b-2014ddc77094",
-                    "type": "input_labels_added"
-                },
-                {
-                    "created_on": "2018-07-06T12:30:06.123456789Z",
-                    "step_uuid": "8720f157-ca1c-432f-9c0b-2014ddc77094",
-                    "text": "can't execute action in session without a contact",
-                    "type": "error"
-                },
-                {
-                    "created_on": "2018-07-06T12:30:08.123456789Z",
-                    "step_uuid": "8720f157-ca1c-432f-9c0b-2014ddc77094",
-                    "text": "can't execute action in session without a contact",
-                    "type": "error"
-                }
-            ],
-            "segments": [],
-            "session": {
-                "environment": {
-                    "date_format": "YYYY-MM-DD",
-                    "input_collation": "default",
-                    "number_format": {
-                        "decimal_symbol": ".",
-                        "digit_grouping_symbol": ","
-                    },
-                    "redaction_policy": "none",
-                    "time_format": "hh:mm",
-                    "timezone": "America/Los_Angeles"
-                },
-                "input": {
-                    "channel": {
-                        "name": "Android Channel",
-                        "uuid": "57f1078f-88aa-46f4-a59a-948a5739c03d"
-                    },
-                    "created_on": "2000-01-01T00:00:00Z",
-                    "text": "Ryan Lewis",
-                    "type": "msg",
-                    "urn": "tel:+12065551212",
-                    "uuid": "9bf91c2b-ce58-4cef-aacc-281e03f69ab5"
-                },
-                "runs": [
-                    {
-                        "created_on": "2018-07-06T12:30:00.123456789Z",
-                        "events": [
-                            {
-                                "created_on": "2018-07-06T12:30:02.123456789Z",
-                                "msg": {
-                                    "channel": {
-                                        "name": "Nexmo",
-                                        "uuid": "57f1078f-88aa-46f4-a59a-948a5739c03d"
-                                    },
-                                    "text": "Ryan Lewis",
-                                    "urn": "tel:+12065551212",
-                                    "uuid": "9bf91c2b-ce58-4cef-aacc-281e03f69ab5"
-                                },
-                                "step_uuid": "8720f157-ca1c-432f-9c0b-2014ddc77094",
-                                "type": "msg_received"
-                            },
-                            {
-                                "created_on": "2018-07-06T12:30:04.123456789Z",
-                                "input_uuid": "9bf91c2b-ce58-4cef-aacc-281e03f69ab5",
-                                "labels": [
-                                    {
-                                        "name": "Spam",
-                                        "uuid": "3f65d88a-95dc-4140-9451-943e94e06fea"
-                                    }
-                                ],
-                                "step_uuid": "8720f157-ca1c-432f-9c0b-2014ddc77094",
-                                "type": "input_labels_added"
-                            },
-                            {
-                                "created_on": "2018-07-06T12:30:06.123456789Z",
-                                "step_uuid": "8720f157-ca1c-432f-9c0b-2014ddc77094",
-                                "text": "can't execute action in session without a contact",
-                                "type": "error"
-                            },
-                            {
-                                "created_on": "2018-07-06T12:30:08.123456789Z",
-                                "step_uuid": "8720f157-ca1c-432f-9c0b-2014ddc77094",
-                                "text": "can't execute action in session without a contact",
-                                "type": "error"
-                            }
-                        ],
-                        "exited_on": "2018-07-06T12:30:10.123456789Z",
-                        "flow": {
-                            "name": "Registration Flow",
-                            "uuid": "8ca44c09-791d-453a-9799-a70dd3303306"
-                        },
-                        "modified_on": "2018-07-06T12:30:10.123456789Z",
-                        "path": [
-                            {
-                                "arrived_on": "2018-07-06T12:30:01.123456789Z",
-                                "exit_uuid": "28039554-80a8-4a5d-9024-90ec1dacdd47",
-                                "node_uuid": "a58be63b-907d-4a1a-856b-0bb5579d7507",
-                                "uuid": "8720f157-ca1c-432f-9c0b-2014ddc77094"
-                            }
-                        ],
-                        "status": "completed",
-                        "uuid": "692926ea-09d6-4942-bd38-d266ec8d3716"
-                    }
-                ],
-                "status": "completed",
-                "trigger": {
-                    "environment": {
-                        "date_format": "YYYY-MM-DD",
-                        "input_collation": "default",
-                        "number_format": {
-                            "decimal_symbol": ".",
-                            "digit_grouping_symbol": ","
-                        },
-                        "redaction_policy": "none",
-                        "time_format": "hh:mm",
-                        "timezone": "America/Los_Angeles"
-                    },
-                    "flow": {
-                        "name": "Registration",
-                        "uuid": "8ca44c09-791d-453a-9799-a70dd3303306"
-                    },
-                    "msg": {
-                        "channel": {
-                            "name": "Nexmo",
-                            "uuid": "57f1078f-88aa-46f4-a59a-948a5739c03d"
-                        },
-                        "text": "Ryan Lewis",
-                        "urn": "tel:+12065551212",
-                        "uuid": "9bf91c2b-ce58-4cef-aacc-281e03f69ab5"
-                    },
-                    "params": {
-                        "coupons": [
-                            {
-                                "code": "AAA-BBB-CCC",
-                                "expiration": "2000-01-01T00:00:00.000000000-00:00"
-                            }
-                        ]
-                    },
-                    "triggered_on": "2000-01-01T00:00:00Z",
-                    "type": "msg"
-                },
-                "type": "messaging",
-                "uuid": "d2f852ec-7b4e-457f-ae7f-f8b243c49ff5"
-            }
-        }
-    ],
-    "resumes": [],
-    "trigger": {
-        "environment": {
-            "date_format": "YYYY-MM-DD",
-            "languages": [
-                "eng"
-            ],
-            "time_format": "hh:mm",
-            "timezone": "America/Los_Angeles"
-        },
-        "flow": {
-            "name": "Registration",
-            "uuid": "8ca44c09-791d-453a-9799-a70dd3303306"
-        },
-        "msg": {
-            "channel": {
-                "name": "Nexmo",
-                "uuid": "57f1078f-88aa-46f4-a59a-948a5739c03d"
-            },
-            "text": "Ryan Lewis",
-            "urn": "tel:+12065551212",
-            "uuid": "9bf91c2b-ce58-4cef-aacc-281e03f69ab5"
-        },
-        "params": {
-            "coupons": [
-                {
-                    "code": "AAA-BBB-CCC",
-                    "expiration": "2000-01-01T00:00:00.000000000-00:00"
-                }
-            ]
-        },
-        "triggered_on": "2000-01-01T00:00:00.000000000-00:00",
-        "type": "msg"
-    }
-}
\ No newline at end of file