API documentation (v. 1.0.1)

Table of Contents

  1. Basics
  2. Terms
  3. Authentication
  4. Endpoint: Profile
  5. Endpoint: Pulse

Basics #

Beta

For all your API testing needs, especially when testing adding new XP into the system, use the beta instance.

API version

The API follows Semantic Versioning. That means bugfixes bump the patch version, backwards compatible features bump the minor version and backwards incompatible changes bump the major version.

Protocol

All requests are made using HTTP and use JSON to serialize the data. Requests with payloads should send the payload as JSON in the request body. The responses are JSON, error responses will contain the key error which has the error message as value. The error message is a human readable message, not intended for automated parsing.

Path

All API paths are prefixed with /api.

HTTPS

All requests must be secured using HTTPS. Plain HTTP requests will not be accepted.

Timestamps

Datetime timestamps should be sent as RFC 3339 datetime values and include the timezone offset. Example: 2016-04-24T01:43:56+12:00.

Terms #

XP

Programming gives users experience points, i.e. XP. A certain amount of XP corresponds to a certain level. In Code::Stats, each keystroke that creates or deletes a character gives 1 XP, so a client should keep an XP counter and add to it on each such keystroke. Operating on many characters at once, such as when deleting highlighted text, is counted as 1 XP, if made possible by the editor's API. An XP value is a positive integer.

Level

A level is a human readable indicator of progress. It is calculated from XP and its only purpose is to make an amount of XP more meaningful for a user. The read API does not provide levels, so it is the client's job to calculate the levels of a user.

Levels are calculated with the following pseudocode formula:

LEVEL_FACTOR = 0.025

// Get level for given XP
int get_level(float xp) {
  return to_int(floor(LEVEL_FACTOR * sqrt(xp)))
}

For more examples of level calculations, see CodeStats.Language.XPCalculator.

Language

XP is always tied to a language. This can be a programming language, but also some other syntax edited by the users, such as Markdown or JSON. Detecting the active language is the client's responsibility. This can be done, for example, based on the active syntax highlighting language or the file's extension. Languages are referred to by their names as strings.

Pulse

A pulse is a collection of XPs for different languages. Pulses should be added periodically by the client and should contain the languages used and the XPs accumulated since the last pulse. They should also contain the time the pulse was created. This way they can be sent later, if the user's Internet connection is down for the moment. Pulses that are more than a week old will not be accepted by the API.

Authentication #

Code::Stats uses simple token authentication. A single token authenticates both a user and the machine they are using. It is signed to prevent tampering and unauthorized creation of tokens matching other users' information. To generate a token, you need to create a new machine from the Machine control panel. A token looks like this:

SFMyNTY.OEotWWdnPT0jI01qaz0.X0wVEZquh8Ogau1iTtBihYqqL71FD8N6p5ChQiIpaxQ

In all API requests that require authentication, the token must be added using the HTTP header X-API-Token. Here is an example request sent with the token (as printed by curl):

> POST /api/my/pulses HTTP/1.1
> Host: codestats.net:443
> User-Agent: curl/7.43.0
> Accept: */*
> X-API-Token: SFMyNTY.OEotWWdnPT0jI01qaz0.X0wVEZquh8Ogau1iTtBihYqqL71FD8N6p5ChQiIpaxQ
> Content-Type: application/json
> Content-Length: 113

Endpoint: Profile #

Read public user's information

GET /api/users/username

Retrieves the information of the specified user. If the user does not exist or their profile is private, HTTP 404 will be returned. An example request that also shows the returned data format:

GET /api/users/Nicd HTTP/1.1

{
  "user": "Nicd",
  "total_xp": 3073421,
  "new_xp": 3289,
  "machines": {
    "Työkone": {
      "xps": 246116,
      "new_xps": 3289
    },
    "Best Machine": {
      "xps": 2827305,
      "new_xps": 0
    }
  },
  "languages": {
    "💩": {
      "xps": 1767278,
      "new_xps": 0
    },
    "XML": {
      "xps": 15,
      "new_xps": 0
    },
    "Plaintext": {
      "xps": 3429,
      "new_xps": 3289
    }
  },
  "dates": {
    "2016-12-31": 3289,
    "2016-08-24": 26700
  }
}

Returns user profile data as JSON. In the data, new_xps is XP gained in the last 12 hours.

Endpoint: Pulse #

Add pulse (authn required)

POST /api/my/pulses

Adds a new Pulse to the system. The client should send a pulse periodically when the user is programming. The payload should be a list of languages and their accumulated XP since the last successful pulse. The pulse must also contain a coded_at timestamp that signifies when the pulse was generated. This allows sending the pulse later, if the Internet connection is momentarily cut.

Note: The coded_at timestamp MUST be in the user's local time with their local UTC offset. Do not convert the time to UTC before sending it to the API.

Note: The coded_at timestamp must be no older than a week. Any older timestamps will result in an error. Timestamps in the future will be ignored, their coded_at will be set to the current moment.

Example payload:

{
  "coded_at": "2016-04-24T01:43:56+12:00",
  "xps": [
    {"language": "C++",    "xp": 15},
    {"language": "Elixir", "xp": 30},
    {"language": "EEx",    "xp": 3}
  ]
}

Returns HTTP 201 and {"ok": "Great success!"} on success.