REST API

The REST API allows devices to access the data.

We use a short notation of the API endpoints in this documentation, to get the actual URI of a resource just prefix the domain name and the /api path like the examples below. This is a general rule that might change depending on the project.

https://example.com/api/delegate
https://example.com/api/session/check-in

Request Format

The system understand requests sent in the following formats:

  • JSON (preferred, default) when the Content-Type header of your request is set to application/json;
  • XML when the Content-Type header of your request is set to application/xml or text/xml;
  • Form Encoded when the header of your request is set to application/x-www-form-urlencoded;
  • Form Multipart when the header of your request is set to multipart/form-data.

Tip

Form Encoded and Form Multipart should be avoided if possible. This is mainly for their inexpressive and overcomplicated representation, good for web forms, bad for humans and debugging.

Take the following as an example, which representation do you find more readable and concise?

{
  "delegate_id": 3,
  "questionnaire_code": "ee86134a-7418-4cf9-8500-4a86353ffab7",
  "answers": [
    { "question_id": 1, "answer_id": 1, "value": "Yes" },
    { "question_id": 2, "answer_id": 3, "value": "Pizza" },
    { "question_id": 3, "value": "I can write anything I want in here" }
  ]
}
delegate_id=1&
questionnaire_code=ee86134a-7418-4cf9-8500-4a86353ffab7&
answers%5Ba%5D%5Bquestion_id%5D=1&
answers%5Ba%5D%5Banswer_id%5D=1&
answers%5Ba%5D%5Bvalue%5D=Yes&
answers%5Bb%5D%5Bquestion_id%5D=2&
answers%5Bb%5D%5Banswer_id%5D=3&
answers%5Bb%5D%5Bvalue%5D=Pizza&
answers%5Bc%5D%5Bquestion_id%5D=3&
answers%5Bc%5D%5Bvalue%5D=I%20can%20write%20anything%20I%20want%20in%20here

Both examples have been formatted to improve readability, the actual request body should strip all unnecessary spaces

Response Format

You can pick the response format simply appending a dot . and the format name of your choice, one in: json, (preferred, default) xml.

GET /delegate/id:820
GET /delegate/id:820.json
GET /delegate/id:820.xml

The first two calls in the example above will return the very same response, JSON encoded.

Response Status Code

The response status code is possibly the most important bit of a response because it can tell you at a glance if what you asked for has been processed correctly or not. All API methods will return an appropriate HTTP status code ranging between 200 and 431, but if something unforeseeable happens they might return an HTTP status code ranging between 500 and 510.

Error Responses

An error response will look like this:

{
  "code": 404,
  "message": "Not Found",
  "errorCode": 0
}
code
This matches the HTTP status code.
message
Brief description of the error.
errorCode
Some API methods return a specific error code that uniquely identify a particular scenario. Some time an error code is guarantee to be returned if a determined set of circumstances does happen.

When the unexpected does happen…

While it’s always a good idea to parse the response looking for the data you’ve requested or for an error message, when the status code is a 5xx you might get back an HTML body or an empty body. This mainly happens when your call does not even get to be processed because something unexpected happens before. As a rule of thumb you can try and parse a 5xx response body, but be prepared to fail gracefully if that fails.

Common 5xx HTTP status codes are:

  • 500 Internal Server Error
  • 501 Not Implemented
  • 502 Bad Gateway
  • 503 Service Unavailable
  • 504 Gateway Timeout
  • 505 HTTP Version Not Supported

Tip

The best way of dealing with an unexpected error is to back off for a while and then try again. The back off timeout should be tuned depending on the method you are calling. If it’s vital that your call gets processed ASAP 500ms or 1s could be good choices, if your call is not high priority you can put it at the end of your queue for later processing or wait a few seconds before trying again.

Interaction Types

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.
connect
Indicates the intent of a delegate to connect with another delegate. Depending on the instance a single connect could trigger a connected event (see below) but usually for such event to trigger both delegates need to connect with each other first.
connected [1]
This is usually a system generated interaction that triggers when two delegates connect with each other.
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.
seen
This is used only by UHF readers.
entered, exited [1]
This allow to detect how many delegates are in a particular area at an event.

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

Interaction [2] 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)
[1](1, 2) You should never have to log such interaction, depending on the configuration for your particular instance of Satellite Tag the system will automatically log this events.
[2]R: Registration app, M: Mobile app, S: Mobi site/Exhibitor portal, U: UHF readers

Barcode

A delegate barcode is generated according to a custom algorithm created to guarantee:

  • uniqueness: if all the guidelines and conditions are met, codes might repeat after 5.6 years from the epoch start. This should give plenty of time since all our projects so far ran over a period of few hours to 6 months maximum.
  • order: the generated codes are ordered based on the time they were generated with an accuracy of few milliseconds if all clocks on the clients are in sync, if they aren’t a few seconds of accuracy or more has to be expected.
  • readability: codes related to the same group (or purchase in case of tickets) are easily identified because the share the same base (the first 12 characters of the barcode). Also, codes that belong to a sequence can be ordered or counted using the last two characters.
  • strength: codes are protected by a check-sum that can detect (but not correct) tampering. The chances of generating a valid barcode without knowing the secret are negligible for our use cases.
  • information rich: by analysing the barcode we can extrapolate meaningful data like:
    • time of the barcode generation, with an error of ±3 minutes
    • if it’s been generated by the server or a client
    • which client generated it (if generated by a client)
    • validity
    • position in a sequence (if the barcode belongs to a group or sequence)

Barcode Parts

Each barcode can be seen as made of three components:

  • base: the first 12 chars. The base doesn’t change within the same group or sequence or purchase;
  • index: the last 3 chars, or 15 bits. They represent the position of this barcode in the sequence;
  • check: the 3 chars between the base and the index, 15 bits. This is a check-sum that signs the “base + index” using a secret shared only among the server and clients.

The base is composed of (in order):

  • days: 11 bits (2 chars + 1 bit)
  • minutes: 9 bits (1 char and 4 bits)
  • milliseconds: 15 bits (3 chars)
  • use epoch: 1 bit
  • random: 9 bits (1 char and 4 bits)
  • server: 1 bit
  • seed: 14 bits (2 chars and 4 bits)

See the below table for more details on the barcode parts.

Char Bits Part Description
0 0-4 Days Number of days since the epoch. Because of the 10 bit size, this will repeat approximately every 5.6 years.
1 5-9
2 10
11-14 Minutes Number of minutes today, 3 minutes approximation.
3 15-19
4 20-24 Milliseconds Number of milliseconds in the current 3 minutes frame, 5ms approximation. This helps keeping the barcodes ordered by creation time and allows generating 182 “ordered” barcodes per second. The number of barcodes that can be generated per second is actually much higher (in the order of 10¹⁸/s with the maximum number of clients generating barcodes simultaneously).
5 25-29
6 30-34
7 35 Use Epoch This is 0 if the barcode has been generated without an epoch. The epoch is not stored in the barcode.
36-39 Random This is a random number between 0 and 512.
8 40-44
9 45 Server This is 1 if the barcode has been generated on the server.
46-49 Seed This is the seed that identifies the device that generated the barcode.
10 50-54
11 55-59
12 60-64 Check These are 15 check bits to prevent tampering or third party barcode generation. The system secret is required to generate a valid “check”.
13 65-69
14 70-74
15 75-79 Index When grouping barcodes, for example tickets of the same purchase, this field allow to index them. This can index from 1 to 803. First 99 barcodes are suffixed with 00-99, after that the entire pool of character is used and the following indexes will look like: A0, A1, A2, … , Z9, ZA, ZB, … , ZX, ZY, ZZ.
16 80-84
17 85-89

Barcode Generation

Using a common interface for the barcode generation across devices and programming language can help keeping the documentation consistent and improve the understanding of the internals.

The current API can be summarised as follows, using pseudo code:

/**
 * Generate a new base for this client or server.
 *
 * @Parameter "seed" The client seed, must be unique per client
 * @Parameter "epochStart" Unix timestamp of the midnight of the first day for the barcode generation, or -1 for a random epoch
 * @Parameter "history" Previously generated bases, it's recommended that these are at least those generated in the past 3 minutes
 * @Parameter "serverGenerated" Should be true only on the server
 * @Return    The array of bits for the base just generated
 */
function generateBase(int seed, int epochStart = -1, bit[][] history = [], bool serverGenerated = false): bit[]

/**
 * Generate a new barcode for the given index and base, signed with the given secret.
 *
 * @Parameter "index" The position of this barcode in a sequence, or zero if the barcode doesn't belong to one
 * @Parameter "secret" The shared secret to sign the barcode check-sum with
 * @Parameter "base" The base to use for the barcode generation, or a random one if not provided
 * @Return    The array of bits for the barcode just generated
 */
function generateBarcode(int index = 0, string secret = '', bit[] base = []): bit[]

/**
 * Generate a new barcode signed with the given secret.
 *
 * @Parameter "secret" The shared secret to sign the barcode check-sum with
 * @Return    The array of bits for the barcode just generated
 */
function generateSecure(string secret = '')

The reference implementation is in PHP, a JavaScript one will soon be implemented.