EasyJam Export API Spec

Mike Kasprzak edited this page Dec 4, 2017 · 5 revisions

NOTE: This specification is out-of-date, and will be updated later. Sorry for the inconvenience.


EasyJam is a specification for an Export format we can easily import in to the Ludum Dare games Database. Ludum Dare will also be providing EasyJam access to our Data.

API Spec Version: EasyJam v0.1 DRAFT *

Got any Feedback or Questions? Did I miss something? Contact Mike.

Elements of an EasyJam API

EasyJam calls are designed to return a list games created by 1 or more users. By design, the API is opt-in, as users must provide a unique token that identifies themselves.

  • Requests made using HTTP GET or HTTP POST (support for both is optional)
  • User Tokens
  • Request URLs
  • Response Format (JSON)

Light Usage

EasyJam APIs are designed to be used lightly. If you're syncing large amounts of data, try not to do it more than once a day. And if you need to make multiple requests, space them out a bit (say every 10 seconds). Be chill about it.

User Tokens

To help us uniquely identify users, we ask that you generate a Token for your users. We'll use this Token to request information about the users.

You can build your Token any way you like, it just needs to be unique per user.

TIP: For a Quick Start, you can simply use the User Name or User ID as your Token, and skip to the next section (Request URLs).

Though it's not necessary, we would strongly encourage you to generate a Secure Token instead of just using a User Name or User Id as the Token.

We're not doing bank transactions here, so the Secure Token can be as simple combination of a User Id and an MD5 hash of their e-mail address. That said, the more unique your token is, the more secure it is.

On your end, you need to be able to reliably reproduce the Secure Token. Either store it in your database, or generate it on demand.

Tokens should only contain letters (A-Z and a-z, no accents), numbers (0-9), underscores (_), dashes (-), and dots (.). See query strings.

Tokens should be no larger than 190 characters. See the Maximum Index Length of InnoDB tables in the MySQL Documentation (i.e. 191 characters for utf8mb4).

Example Secure Token: myjam_username_1f3870be274f6c49b3e31a0c6728957f

The example above contains a User Name, but a User ID would work as well (it might even be faster/simpler for you to look up).

The example also includes a Unique Name "myjam_", which can be used to autodetect what the token belongs to.

Here is some sample code for generating and validating Secure Tokens.

// PHP Example for an MD5 Hashed e-mail token.
// So it's less obvious, we add a unique phrase to the e-mail as a salt.
function GeterateToken( $user ) {
    // This assumes $user['name'] meets the token criteria above 
    return "myjam_" . 
        $user['name'] . "_" . 
        md5( 
            $user['email'] +
            $user['date_created'] +
            "a VERY salty hash Salt... Mmmm... saaalt"
        );
}

// An example function for extracting the username
function GetUserNameFromToken( $token ) {
    // This assumes $user['name'] doesn't contain underscores
    $part = explode("_",$token);

    if ( count($part) === 3 )
        return $part[1];
    return null;
}

// An example function for validating a token for a user
function ValidateToken( $user, $token ) {
    // This assumes $user['name'] does not contain underscores
    $part = explode("_",$token);

    if ( count($part) !== 3 )
        return false;
    
    if ( $part[0] !== "myjam" )
        return false;

    if ( $part[1] !== $user['name'] )
        return false;

    $salted_email = 
        md5( 
            $user['email'] +
            $user['date_created'] +
            "a VERY salty hash Salt... Mmmm... saaalt"
        );

    return $salted_email === $part[2];
}

// Alternatively, you can generate a unique code.
// Store this in your DB, and use a copy instead of the MD5 hash above.
// Then as needed, you can generate a new token to invalidate old ones.
function GenerateUniqueTokenValue( $num_characters = 32 ) {
    return bin2hex(openssl_random_pseudo_bytes($num_characters / 2));
}

In the example above, our Secure Tokens are made unique by creating an MD5 Hash of a user's e-mail address, the date their user account was created, and an uncommon phrase (salt).

Token Security Tips

We recommend that you DON'T use a user's password in your Security Tokens. We're not actually securing the transaction, just uniquely identifying them in a secure way. If the Secure Token contains a password, then once your algorithm is known, it becomes possible to brute-force dictionary attack the password. That wont work for all of your passwords, but it will for some. Not cool. Plus, passwords change more often than e-mail addresses, and a user will have to refresh their key any time they change their password.

If using dates in an MD5 hash, make sure it's a date that will not change (i.e. not a modified date). If you do, your Secure Token will invalidate itself any time a change occurs. Prefer the user creation date or activation date (preferably one you never tell anyone, to make it harder to reverse engineer).

How to use Tokens

The easiest way to add token support is to have a private place where a user can check their token. This is probably a user settings or config page.

It's recommended that you don't display tokens by default (and show them with a button click). Unless you've gone through the trouble of generated unique values for each token, it can be extremely unpleasant dealing with a leaked key.

Similarly, to input a token on a client website, you can provide a field in a users settings or config page. Alternatively, you could have a generic "Paste a Token Here" field, and autodetect who it belongs to based on the Unique Name.

Request URLs

Single User Requests

You can do whatever you want, but you're encouraged to use a format like the following.

http://api.myjam.com/?token=myjam_username_8d1284d18uoa8deou9adou

Alternatively, pass the query string via HTTP POST.

Multi User Requests

You can format them however you like (query arrays, delimiters).

http://api.myjam.com/?token[]=myjam_user1_83dou&token[]=myjam_user2_dou9u

The above is PHP friendly, automatically combining both tokens in to an array.

Security

For your safety, you should sanitize the tokens before using them. Confirming that a token only contains letters, numbers, dashes, underscores, and dots is an easy way to do it.

Response Format (JSON)

Make sure you're using the Content-Type HTTP header of application/json.

// PHP Version //
header("Content-Type: application/json");

Simple Response Format

As of EasyJam v0.1

This is the core EasyJam response format.

For a minimal implementation of EasyJam, all fields are required.

{
    "myjam_pov_ax372bxago832":{
        "Item":[
            {
                "Id":45,
                "Name":"Some Fantastic Game",
                "Released":"2012-11-25T15:52:01+00:00",
                "Description":"Some Fantastic Description",
                "Image":[
                    "http://imgur.com/some-screen-shot.jpg"
                ],
                "Link":{
                    "Windows":"http://myhost.com/game/fantastic.zip"
                }
            }
        ]
    }
}

Where:

  • myjam_pov_ax372bxago832 is the Token.
  • Item is the Item section. Items are usually games. A user may have more than 1 item, so it's an Array of Objects.
  • Id is an internal unique Id you use for the game. We use this to confirm on our end that we only have 1 copy of the game in our database.
  • Name is the name of the game.
  • Released is when an item was Released or Published. Though not required, you are encouraged to pre-sort your items so the most recently released item is first.
  • Description is a description.
  • Image is an array of Image Links. The first image will likely be the default banner, so sort them accordingly.
  • Link in an array of Links. The Key part is the platform, the value is the URL. If you don't know the platform, use "Download" or numbered names like "Download2" if you need multiple, or "Source" if it's source code.

Date Formats

Dates should be encoded in W3C format. W3C is a subset of ISO-8601 that's generally more compatible. Languages like JavaScript are known to have problems with some ISO-8601 encodings (mainly PHP's default ISO8601 encoding, which neglects a colon in the time zone).

And though W3C/ISO-8601 supports time-zones, you should be using UTC times (i.e. time-zone zero (+00:00) or zulu (Z)).

// PHP Examples

// Unix Time version (i.e. like "time()")
$UnixTime = strtotime("2012-11-25T15:52:01+00:00");  // Decode date string
echo date(DATE_W3C,$UnixTime);                       // Encode date string

// DateTime version
$MyDate = new DateTime("2012-11-25T15:52:01+00:00"); // Decode date string
echo $MyDate->format(DateTime::W3C);                 // Encode date string
// JavaScript Example

// NOTE: Don't forget the 'new'!
var MyDate = new Date('2012-11-25T15:52:01+00:00'); // Decode date string
console.log( MyDate.toISOString() );                // Encode date string
/* MySQL Example */

SELECT CONVERT("2012-11-25T15:52:01+00:00", DATETIME);    /* Encode date string */
SELECT DATE_FORMAT(the_field_to_convert, '%Y-%m-%dT%TZ'); /* Decode date string */
SELECT DATE_FORMAT(CONVERT_TZ(the_field_to_convert, @@session.time_zone, '+00:00'), '%Y-%m-%dT%TZ');
/* CONVERT_TZ might be needed to properly convert to to UTC */

Errors

As of EasyJam v0.1

When a request is made for an invalid Token, return an object with an Error field for that Token. You can include an error message as a string, or true if you want to be unspecific.

{
    "ludumdare_01010111_bax3ru72bx412ud7u02":{
        "Error":"Bad Token"
    }
}

When there's an error unrelated to tokens (or no tokens are provided), you can similarly generate a response with a top-level Error field.

{
    "Error":"Something is wrong"
}

You can also optionally emit an appropriate HTTP Response code (400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found), but this might be a problem for Internet Explorer users (assuming you're doing an XMLHttpRequest).

Extended Response Format

The following optional fields are also available. You're encouraged to include as many of these as makes sense.

{
    "ludumdare_01010111_bax3ru72bx412ud7u02":{
        "Item":[
            {
                /* ... */
                "OriginURL":"https://ludumdare.com/ld/32/game/bed-hogg",
                "Icon":"https://static.ludumdare.com/up/8821/04.png",
                "Banner":"https://static.ludumdare.com/up/8821/02.png",

                "Event":"Ludum Dare 32",
                "EventId":32,

                "Type":"game",
                "SubType":"compo",
                "SubSubType":"unpublished",

                "EmbedURL":"http://somewebsite.com/me/mygame.swf",
                "EmbedOriginURL":"http://somewebsite.com/me/mygame",
                "EmbedWidth":800,
                "EmbedHeight":480,
                "EmbedType":"swf"
            }
        ]
    }
}

As of EasyJam v0.1.

General:

  • OriginURL is the link to the game on the original website.
  • Icon is a typically square image used to showcase an item. Is often resized small.
  • Banner is a wide rectangular image file used to showcase a item. Overrides "1st image banner" behaviour.

Event:

  • Event is the name of the event the item belongs to.
  • EventId is a numeric identifier of an event. May include decimals (eg. Ludum Dare 8.5, Bi-Annual Event 2005.1, Monthly Event 2015.07). Keep sorting order in mind when using decimals (i.e. 2002.2 is bigger than 2002.11).

Types:

  • Type is a website specific classification for an item. Ludum Dare uses lower case names for types, but this is not required.
  • SubType is a website specific 2nd level classification for an item.
  • SubSubType is a website specific 3rd level classification for an item.

Embedding:

  • EmbedURL is the raw URL of a file or link to embed.
  • EmbedOriginURL is the original website link to the data we are embedding.
  • EmbedWidth is is the width of the embed frame.
  • EmbedHeight is is the height of the embed frame.
  • EmbedType if it's not obvious what data is being embedded (but you know).
    • swf for Flash
    • unity for Unity Player
    • java, silverlight
    • html for HTML5 and most other things

User Section

As of EasyJam v0.1 (optional)

The User Section is optional, but it can be used to share other useful data.

{
    "ludumdare_01010111_bax3ru72bx412ud7u02":{
        "User":{
            "Id":148,
            "Name":"01010111",
            "OriginURL":"https://ludumdare.com/user/01010111",
            "Icon":"https://static.ludumdare.com/img/upload/41931/01.png"
        },
        "Item":[
            /* Games go here */
        ]
    }
}

As of EasyJam v0.1:

  • Id is the website specific Unique User Id.
  • Name is the user's name.
  • OriginURL is the URL of the user-page on the original website.
  • Icon is an avatar image file. Usually square.

Info Section

As of EasyJam v0.1 (optional)

The Info Section is option, but can be included to provide user and item unspecific information.

{
    "Info":{
        "Version":0.1,
        "ResponseTime":"2015-11-11T15:52:01Z",
        "More":10
    },
    "ludumdare_01010111_bax3ru72bx412ud7u02":{
        /* User Data Goes Here */
    }
}

As of EasyJam v0.1:

  • Version is the version of the EasyJam spec it conforms to.
  • ResponseTime is when the response was generated.
  • More is used by APIs with limits. It tells you More items are available (see More Field)

More Field

As of EasyJam v0.1 (optional)

For websites with limits, a special More field is used to say a user has more items than the prior request fulfilled.

More will contain the offset to the next set of items. You can pass it in the query string parameter "offset" to get additional entries.

All More offset values returned by a request will be the same for all items in the request. Simply check for the existence of More, and make a new request for all entries that need more data.

{
    "Info":{
        "More":10
    },
    "ludumdare_01010111_bax3ru72bx412ud7u02":{
        "Item":[
            /* Games go here */
        ],
        "More":10
    }
}

Custom Section

As of EasyJam v0.1 (optional)

You can optionally include an extra section in all Info, User, and Item sections containing additional information specific to your event. The section is usually white-space removed version of your event's name, but an acronym is also acceptable.

Section Names should only contain letters (A-Z and a-z, no accents) and numbers (0-9).

Inside your section, you are free to name your fields whatever you like, but you are encouraged to use the "UpperCaseCaps" styling used throughout the spec.

If we start noticing many events are sharing similar fields, we may promote the field the Extended Response Format.

{
    "Info":{
        "Version":0.1,
        "ResponseTime":"2015-11-11T15:52:01Z",
        "LudumDare":{
            "Blah":"BlahBlah"
        }
    },
    "ludumdare_01010111_bax3ru72bx412ud7u02":{
        "User":{
            "Name":"01010111",
            "LudumDare":{
                "Coolness":1000000
            }
        },
        "Item":[
            {
                /* Other items go here */
                "LudumDare":{
                    "Love":1000,
                    "Favourites":442
                }
            }
        ]
    }
}

Advanced Features

API Keys

As of EasyJam v0.2 (unfinished)

API Keys are optional, but add an extra layer of security. They also allow you to enforce usage limits (if needed).

TODO: Discuss how to set up and provide an API key.

  • Required in all requests
  • Recommend using query parameter "key".
  • Recommend using HTTP POST instead of HTTP GET
  • Generate key in a similar way to Secure Tokens (but you should actually store it and not calculate it using an MD5)

Automatic User Token Ingestion

As of EasyJam v0.3 (unfinished)

TODO: this

Response DIFFs

As of EasyJam v0.4 (unfinished)

TODO: Discuss optional DIFF format

(i.e. passing in modified dates, and if there are no changes since that date, omit that response)

When implementing Response DIFFs, emit a ResponseTime in the Info section, and use the value from that request for all your future DIFF requests (until it is invalidated by a response).

Appendix: API Compatibility

Generally speaking, the EasyJam Spec should be forward and backwards compatible, new features are simply ignored. Any API breaking changes will be documented here.

Roadmap, and previous versions:

  • EasyJam v0.4 (unfinished) - Support for Revision DIFFs.
  • EasyJam v0.3 (unfinished) - Support for Automatic Token Retrieval and Ingestion.
  • EasyJam v0.2 (unfinished) - API Key Reference and Spec.
  • EasyJam v0.1 - Basic Specification. Tokens are exchanged manually.

Appendix: Web API Information

The following section contains details on the implementations of the EasyJam API on various website.

  • Section Name: the name used for custom data sections
  • Token Unique: (optional) if your tokens contain a unique string to help clients autodetect the source.
  • HTTP GET or POST: whether you support GET, POST, or BOTH.
  • API Key Required: whether your API requires a key for access.
  • Multiple Token Support: (optional) if you support multiple tokens in a single request.
  • Response Limits: (optional) whether the website imposes limits on tokens or items per request.
  • Maximum Tokens Per Request: (optional) The maximum number of tokens that will be processed in a single request.
  • Maximum Items Per User Per Request: (optional) The maximum number of items each response will return PER USER!

Ludum Dare

Please DON'T make API requests within 3 hours before or after our theme announcements (i.e. 9:00 PM ET, 6:00 PM PT, 1:00 and 2:00 UTC).

  • Section Name: LudumDare
  • Token Unique: ldjam_
  • HTTP GET or POST: BOTH (though we may switch to POST only)
  • API Key Required: YES, generate a key inside your user settings.
  • Multiple Token Support: YES, include multiple query parameters named 'token[]'
  • Response Limits: YES, use query parameter 'offset' to feed offsets
  • Maximum Tokens Per Request: TBD
  • Maximum Items Per User Per Request: TBD

Base URL: http://api.ludumdare.com/v1/easyjam (NOTE: API is not live yet)

To request specific information, append the following URLs.

  • / (Fetch Ludum Dare games)
  • /ld/ (Fetch Ludum Dare main event games only)
  • /minild/ (Fetch MiniLD games only)
  • /all/ (Fetch All games)
  • /other/ (Fetch Other games only)
  • /other/jam/1gam/ (Fetch Other games specifically from 1GAM)

Users can retrieve their Token by TBD.

Tokens can be ingested automatically via TBD.

Event Strings

  • "Ludum Dare ??"
  • "MiniLD ??"
  • "October Challenge ????"

Types

  • 'game' - Games
  • 'demo' - Graphical Demos and Intros (see Demoscene)
  • 'media' - Audio or Video
  • 'craft' - Physical things
  • 'other' - anything else (hopefully we don't need this)

SubTypes

  • 'jam' - Entries in the Jam category
  • 'compo' - Entries in the Compo category
  • 'craft' - Entries in the Craft Jam category
  • 'late' - Late entries and themed games not part of the original event
  • 'remake' - Post Compo/Post Jam entries, Remakes and Sequels

One Game a Month (1GAM)

TODO