Interaction

Object Shape

An interaction object will always have this shape when not embedded into other objects:

{
  "id": "integer",
  "_type": "string",
  "firstDelegate": "object:delegate",
  "secondDelegate": "object:delegate",
  "thirdDelegate": "object:delegate",
  "firstDevice": "object:device",
  "secondDevice": "object:device",
  "thirdDevice": "object:device",
  "interactionType": "string",
  "data": "object",
  "recordedAt": "string",
  "createdAt": "string",
  "updatedAt": "string"
}

The data property inside the interaction object does not have a configurable structure and will depend on the type of interaction. For example a lead interaction will have all answers loaded in this property.

Posting Form Answers

When an interaction captures a form submission (for example a lead capture, a poll vote, a registration form or any custom form-based interaction), answers must be posted following the convention below. The backend will automatically reshape them into the structures expected by reporting, exports and downstream consumers.

This convention applies to every source — REST API, mobi/rego sites, portal, batch imports, when-then actions — because the reshaping happens in a single event subscriber, regardless of where the interaction was created.

Input shape

Send the form payload inside the interaction’s data property:

{
  "firstDelegate": 789,
  "firstDevice": 85,
  "data": {
    "form": "leadcapture",
    "answers": [
      {
        "field": "name",
        "fieldLabel": "Full Name",
        "fieldType": "text",
        "answer": "Jane Doe"
      },
      {
        "field": "company",
        "fieldLabel": "Company",
        "fieldType": "text",
        "answer": "Acme Corp"
      },
      {
        "field": "contact.email",
        "fieldLabel": "Email Address",
        "fieldType": "email",
        "answer": "jane@satellite.test"
      },
      {
        "field": "contact.phone",
        "fieldLabel": "Phone Number",
        "fieldType": "tel",
        "answer": "+64 21 555 0000"
      }
    ]
  }
}

Required keys inside data:

  • form — a string identifier for the form (used to disambiguate when an interaction type has multiple forms).

  • answers — a flat list of answer objects.

Each answer object supports:

Key

Required

Description

field

yes

The field path. Use dot-notation (contact.email) for nested answers; a single segment (name) for top-level answers.

answer

yes

The submitted value (string, number, boolean, array — whatever the field type produces).

fieldLabel

no

Human-readable label. Used as the key in reportData for exports and reports. Falls back to field when missing.

fieldType

no

The form field type (text, checkbox, select, …). Carried through for consumers that need it; not used by the reshaping logic.

How the backend reshapes the payload

After the interaction is received, the original data is augmented with two derived structures:

  • answersData — the answers reassembled into a nested object that mirrors the dot-notation paths. Use this when reading answers programmatically.

  • reportData — a flat label => value map. Use this for exports, CSV/XLSX downloads and reports where the human-readable label is what matters.

The original form and answers keys are preserved untouched.

Given the request above, the stored data becomes:

{
  "form": "lead-capture",
  "answers": [
    { "field": "name", "fieldLabel": "Full Name", "fieldType": "text", "answer": "Jane Doe" },
    { "field": "company", "fieldLabel": "Company", "fieldType": "text", "answer": "Acme Corp" },
    { "field": "contact.email", "fieldLabel": "Email Address", "fieldType": "email", "answer": "jane@acme.test" },
    { "field": "contact.phone", "fieldLabel": "Phone Number", "fieldType": "tel", "answer": "+64 21 555 0000" }
  ],
  "answersData": {
    "name": "Jane Doe",
    "company": "Acme Corp",
    "contact": {
      "email": "jane@acme.test",
      "phone": "+64 21 555 0000"
    }
  },
  "reportData": {
    "Full Name": "Jane Doe",
    "Company": "Acme Corp",
    "Email Address": "jane@acme.test",
    "Phone Number": "+64 21 555 0000"
  }
}

Notes and gotchas

  • Always send the flat answers array. Do not pre-build answersData or reportData — the subscriber will skip reshaping if answersData is already present in the payload, leaving an inconsistent record.

  • Field paths are dot-separated, not bracketed. Use contact.email, not contact[email].

  • Answers without a field are silently skipped; make sure every answer object has one.

  • form is required for the reshaping to run. An interaction with answers but no form will be stored as-is, with no answersData / reportData.

  • The convention is the same for POST /interaction/new/{type} and POST /interaction/batch.

Interaction Types

This list will be updated, it’s NOT UP TO DATE.

A non exhaustive list of the standard interactions is:

check-in

Indicates someone has checked in into a session or interacted with any device. It’s the most common action you can use anywhere you don’t need to define custom behaviour or attach complex data to it.

check-out

The opposite of a check-in, when someone leaves a session. Currently used to enable “single-entry” sessions.

join

Usually referred to a session, it’s a broad term and generally means that delegate is supposed to show up at a particular session or that they are allowed into such session.

booking, cancel-booking

These events allow you to book devices. The device you can book should be of a bookable type but this is not enforced. This has been used only for the meeting tables feature so far.

invite, uninvite, notify-invitees

These actions are used to invite delegates to a session and/or to notify them. This most likely requires some custom logic that can be added to the AppBundle.

Some interaction types we’ve used so far are summarised below:

Interaction

[1]

Description

check-in

R M S

A delegate shows up (physically) to an event or session

check-out

M S

A delegate leaves an event or session

join

R M S

A delegate is allowed to, has booked or is assigned to an event, session, …

seen

U

A delegate has been seen at an event or session (usually by a UHF reader)

entered

U

A delegate has entered an area (usually based on where they’ve been seen)

exited

U

A delegate has left an area (usually based on where they’ve been seen)

connect

S

A delegate sends a connection request

connected

S

A delegate has connected with someone (usually when both reciprocally connect)

booking

S

A delegate has booked something like a meeting, session, …

cancel-booking

S

A delegate has cancelled a booking

invite

S

A delegate has invited someone to something

uninvite

S

A delegate has uninvited someone he previously invited

notify-invitee

S

A delegate asks to notify an invitee about something

order

M S

A delegate orders something, usually a drink

order-processing

S

A delegate’s order is being processed

order-cleared

S

A delegate’s order has been completed (usually this fires a text message)

download

M S

A delegate requests to download something

downloaded

M S

A delegate has downloaded something

upload

M

A delegate uploads something

feedback

S

A delegate sends their feedback

competition

S

A delegate enters a competition

voucher

S

A delegate consumes a voucher

question

S

A delegate asks a question

lead

M

A delegate records a lead

poll-vote

S

A delegate votes in a poll

own

M S

A delegate owns a device (usually for sponsors, to identify staff members)

print

R

A delegate’s badge has been printed (usually from the registration app)

GET /interaction/list

Get the list of interactions matching the specified query.

Note

This method supports pagination.

Use limit and offset to paginate returned results. The defaults if no limit or offset are provided are limit=5000 and offset=0.

GET /interaction/list?offset=0&limit=5000

The total result set count is returned in all responses in the meta property. Links to the next or previous page are also provided in the same property.

Searching interactions

Pass a search query parameter with a JSON expression to filter the list. See Searching lists for the full operator reference.

Category

Fields

Direct fields

id, interactionType, linkedId, linkedClass, recordedAt, createdAt, updatedAt

Relations (by ID)

firstDelegate, secondDelegate, thirdDelegate, firstDevice, secondDevice, thirdDevice

Interaction filters

Not supported. Using fi, si or ti on the interaction list returns an error.

Filter by interaction type:

GET /interaction/list?search={"==":["interactionType","check-in"]}

Check-ins for a specific delegate:

GET /interaction/list?search={"and":[{"==":["interactionType","check-in"]},{"==":["firstDelegate",789]}]}

Interactions for a specific device:

GET /interaction/list?search={"==":["firstDevice",85]}

Multiple interactions by ID:

GET /interaction/list?search={"in":["id",[101,102,103]]}

POST /interaction/new/{interactionType}

Creates a new interaction.

See the list of Error Responses for more information, in particular error 1301, 1401, 1501, 1502, 1503.

Some examples for common interactions:

POST /interaction/new/check-in
POST /interaction/new/join
POST /interaction/new/lead
POST /interaction/new/print

Example request to check-in a delegate into a session:

POST /interaction/new/check-in
{
  "firstDelegate": 789,
  "firstDevice": 85,
  "recordedAt": 1696185089
}

Or, because only one delegate and one device are involved:

{
  "delegate": 789,
  "device": 85,
  "recordedAt": 1696185089
}

Example response:

{
  "data": {
    "id": 89027,
    "_type": "interaction",
    "firstDelegate": {
      "id": 789,
      "_type": "delegate"
    },
    "firstDevice": {
      "id": 6,
      "_type": "device"
    },
    "interactionType": "check-in",
    "recordedAt": "2018-03-13T23:31:16+00:00",
    "createdAt": "2018-03-13T23:31:32+00:00",
    "updatedAt": "2018-03-13T23:31:32+00:00"
  },
  "meta": {}
}

POST /interaction/test/{interactionType}

Tests if creating an interaction with the given parameter would succeed or not. This can be used for example to validate tickets before using them.

See the list of Error Responses for more information, in particular error 1301, 1401, 1501, 1502, 1503.

Example request to check-in a delegate into a session:

POST /interaction/test/check-in
{
  "firstDelegate": 789,
  "firstDevice": 85,
  "recordedAt": 1696185089
}

Or, because only one delegate and one device are involved:

{
  "delegate": 789,
  "device": 85,
  "recordedAt": 1696185089
}

Example response:

{
  "data": {
    "id": null,
    "_type": "interaction",
    "firstDelegate": {
      "id": 789,
      "_type": "delegate"
    },
    "firstDevice": {
      "id": 6,
      "_type": "device"
    },
    "interactionType": "check-in",
    "recordedAt": "2018-03-13T23:31:16+00:00",
    "createdAt": "2018-03-13T23:31:32+00:00",
    "updatedAt": "2018-03-13T23:31:32+00:00"
  },
  "meta": {}
}

Example of response in case of failure:

{
  "data": {},
  "meta": {
    "error": {
      "code": 412,
      "message": "Too many interactions",
      "internalCode": 740
    }
  }
}

DELETE /interaction/id:{id}

Delete the given interaction. Returns a no content response (status 204).

Example request to delete interaction 123:

POST /interaction/id:123

POST /interaction/batch

You can send a batch of interaction to this endpoint and they will be processed in order and a ordered collection of responses will be returned.

See the list of Error Responses for more information, in particular error 1301, 1401, 1501, 1502, 1503.

Example request:

POST /interaction/batch
{
  "interactions": {
    "a": {
      "firstDelegate": 1,
      "interactionType": "upload"
    },
    "b": {
      "firstDelegate": 3,
      "firstDevice": 123,
      "interactionType": "check-in"
    }
  },
  "sharedValues": {
    "upload": "iVBORw0KGgoAA[ ... more base64 encoded stuff ... ]oI5NwAAAABJRU5ErkJggg=="
  }
}