Data Tracking API verbs

Plainflow is 100% compliant with the Segment Specs. We support the same verbs that Segment supports.

Note: Plainflow is still not integrated with Segment. If you are a Segment customer, please contact us.

Here’s the list of the API verbs used to send data about people and their interaction with your product:

  • Identify: who is the customer?
  • Track: what are they doing?
  • Page: what web page are they on?
  • Screen: what app screen are they on?
  • Alias: what was their anonymous identity?

Every call shares the same structure.


Every API call has the same core structure and fields. These fields describe user identity, timestamping and mechanical aides like API version.

Here’s an example of these common fields in raw JSON:

  "anonymousId": "59f75a945b289f44d6e8aca5",
  "context": {
    "active": true,
    "app": {
      "name": "Sudoku",
      "version": "125",
      "build": "",
      "namespace": "com.production.sudoku"
    "campaign": {
      "name": "Enigma Now Newsletter",
      "source": "Newsletter",
      "medium": "email",
      "term": "sudoku mania",
      "content": "image link"
    "device": {
      "id": "59FCE116-AD08-4140-94B5-C74B66D8B8FD",
      "advertisingId": "AA279A76-5073-4810-9583-A6C2CA7DB1D3",
      "adTrackingEnabled": true,
      "manufacturer": "Apple",
      "model": "iPhone6,2",
      "name": "pear",
      "type": "ios",
      "token": "afd090fd709f7df0aa0aa097c0c970d7809b709"
    "ip": "",
    "library": {
      "name": "plainflow.js",
      "version": "2.11.0-p1"
    "locale": "en-US",
    "location": {
      "city": "San Francisco",
      "country": "United States",
      "latitude": 40.2964197,
      "longitude": -76.9411617,
      "speed": 0
    "network": {
      "bluetooth": false,
      "carrier": "Sprint",
      "cellular": true,
      "wifi": false
    "os": {
      "name": "iPhone OS",
      "version": "8.1.3"
    "page": {
      "path": "/blog/",
      "referrer": "",
      "search": "",
      "title": "Plainflow Doc",
      "url": ""
    "referrer": {
      "id": "AA5452FBCDDAAA23",
      "type": "Facebook"
    "screen": {
      "width": 320,
      "height": 568,
      "density": 2
    "timezone": "Europe/Amsterdam",
    "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"
  "messageId": "abcfd3218-2ceb-4881-9c73-79a1c782f370",
  "receivedAt": "2017-02-09T04:08:31.909Z",
  "sentAt": "2017-02-09T04:08:31.581Z",
  "timestamp": "2017-02-09T04:08:31.905Z",
  "type": "track",
  "userId": "97980cfea0067",
  "version": 2

In more detail these common fields for every API call are:

anonymousId optionalStringA pseudo-unique substitute for a User ID, for cases when you don’t have an absolutely unique identifier. See the Identities docs for more detail.
context optionalObjectDictionary of extra information that provides useful context about a message, but is not directly related to the API call like ip address or locale. See the Context field docs for more detail.
messageId implicitStringAutomatically collected by Plainflow, a unique identifier for each message that lets you find an individual message across the API.
receivedAt implicitDateAutomatically set by Plainflow, the timestamp of when a message is received by Plainflow is an ISO-8601 date string. See the Timestamps fields docs for more detail.
sentAt optionalDateTimestamp of when a message is sent to Plainflow, used for clock skew correction. It is set automatically by the Plainflow tracking libraries. It is an ISO-8601 date string. See the Timestamps fields docs for more detail.
timestampoptionalDateTimestamp when the message itself took place, defaulted to the current time by the Plainflow Tracking API. It is anISO-8601 date string. If the event just happened, leave it out and we’ll use the server’s time. If you’re importing data from the past, make sure you to provide a timestamp. See the Timestamps fields docs for more detail.
type implicitStringType of message, corresponding to the API method: 'identify', 'track', 'page', 'screen' or 'alias'.
userId requiredStringUnique identifier for the user in your database. A userId or anonymousId is required. See the Identities docs for more detail.
version implicitNumberVersion of the Tracking API that received the message, automatically set by Plainflow.

Beyond this common structure, each API call adds a few specialized top-level fields.

The Context object

Context is a dictionary of extra information that provides useful context about an event, for example the user’s ip address or locale. Context is a complete and explicit specification, so properties outside the spec will be ignored.

You must only use Context fields for their intended meaning.

activeBooleanWhether a user is active. This is usually used to flag an .identify() call to just update the traits but not “last seen.”
appObjectdictionary of information about the current application, containing name, version and build. This is collected automatically from our mobile libraries when possible.
campaignObjectDictionary of information about the campaign that resulted in the API call, containing name, source ,medium, term and content. This maps directly to the common UTM campaign parameters.
deviceObjectDictionary of information about the device, containing id, manufacturer, model, name, type and version.
ipStringCurrent user’s IP address.
libraryObjectDictionary of information about the library making the requests to the API, containing name and version.
localeStringLocale string for the current user, for example en-US.
locationObjectDictionary of information about the user’s current location, containing city, country, latitude, longitude, region and speed.
networkObjectDictionary of information about the current network connection, containing bluetooth, carrier, cellular and wifi.
osObjectDictionary of information about the operating system, containing name and version.
pageObjectDictionary of information about the current page in the browser, containing hash , path , referrer , search , title and url. Automatically collected by Analytics.js.
referrerObjectDictionary of information about the way the user was referred to the website or app, containing type, name, url and link.
screenObjectDictionary of information about the device’s screen, containing density, height and width.
timezoneStringTimezones are sent as tzdata strings to add user timezone information which might be stripped from the timestampEx:America/New_York.
traitsObjectDictionary of traits of the current userThis is useful in cases where you need to track an event, but also associate information from a previous identify call.
userAgentStringUser agent of the device making the request.

Plainflow.js automatically populates the following common fields:

  • campaign.source
  • campaign.medium
  • campaign.term
  • campaign.content
  • hash
  • library.version
  • ip *
  • page.path
  • page.title
  • page.url
  • screen.density
  • screen.height
  • screen.width
  • search
  • title
  • userAgent
  • url

* The IP Address is filled in by our servers only when they receive a message from the Plainflow.js library

Other libraries only collect context.library, any other context variables must be sent manually.


Every API call has three timestamps, timestamp, sentAt and receivedAt.

You need to include both sentAt and timestamp. Internally, we add receivedAt and make clock skew corrections to timestamp using the difference between receivedAt and sentAt.

All timestamps are ISO-8601 date strings.

The timestamp field

This is important to use for importing historical data to the API.

The timestamp timestamp specifies when the datapoint occurred. For track calls it’s the exact time when the user performed an action. Or, for identify calls, this would be when you saw the user.

The timestamp field is settable from our server-side libs or if passing info directly to the HTTP endpoints.

The sentAt field

The sentAt timestamp specifies the clock time for the client’s device when the network request was made to the Plainflow API. For libraries and systems that send batched requests, there can be a long gap between a datapoint’s timestamp and sentAt. Combined with receivedAt, we can use sentAt to correct the original timestamp in situations where a user’s device clock cannot be trusted (mobile phones and browsers). The sentAt and receivedAt timestamps are assumed to occur at the same time (maximum a few hundred milliseconds), and therefore the difference is the user’s device clock skew, which can be applied back to correct the timestamp.

The receivedAt field

The receivedAt timestamp is added to incoming messages as soon as they hit our API. It’s used in combination with sentAt to correct clock skew, and also to aid with debugging libraries and systems that deliver events in batches.

Not using Plainflow yet? Get your free account here. 👈