-
Notifications
You must be signed in to change notification settings - Fork 1
api_structure
- Every exported
elements_...function is a thin wrapper around one HTTP request. - Requests are async and callback-based (GameMaker’s HTTP async event).
- Auth is handled via named tokens (
"auth_bearer","session_secret") that you store once and which are then injected into all requests that need them. - Request bodies are formatted based on a Content-Type → converter mapping.
- Responses go through an optional hook system and then into your callback.
Once you understand that pattern, every endpoint behaves the same way.
All endpoints follow this shape:
function some_elements_endpoint(arg1, arg2, ..., _callback = undefined)
{
// validate arguments
// build URL
// (optionally) build query params
// define required security schemes
return _elements_create_request(_url, _params, _method, _body, _content_type, _security, _callback, _GMFUNCTION_);
}elements_get_application("my-app-id", function(_code, _data, _request) {
if (_code == 200) {
show_debug_message("App: " + string(_data));
} else {
show_debug_message("Error: " + string(_code));
}
});-
elements_get_applicationwill:- Validate its arguments.
- Build URL:
"{rest_url}/application/my-app-id". - Require security:
[ "auth_bearer", "session_secret" ]. - Create an
ElementsRequestandsend()it.
-
It returns the HTTP request id from
http_request(in case you want to track/cancel).
var _body = new ElementsCreateAppleIapReceipt(_receipt_base64, "SANDBOX");
elements_upload_apple_iap_receipt(_body, function(_code, _data, _request) {
if (_code == 200) {
// _data is whatever the server returns (likely JSON parsed to a struct/array)
show_debug_message("Reward issuances: " + string(_data));
} else {
show_debug_message("IAP error: " + string(_code));
}
});Important
When a body is a struct generated by these constructors, the wrapper calls <Type>_validate() before sending to ensure types are correct.
Auth tokens live in the obj_elements_core singleton, in its auth_tokens map:
// set
_elements_request_auth_set_token("auth_bearer", some_token);
_elements_request_auth_set_token("session_secret", some_other_token);
// get
var bearer = _elements_request_auth_get_token("auth_bearer");The singleton is created lazily:
function _elements_get_singleton(_where)
{
static instance = instance_create_depth(0, 0, 0, obj_elements_core);
with (instance) return self;
}So you don’t manually create it; using any of the helper functions will.
Endpoints declare which auth schemes they need:
var _security = [ "auth_bearer", "session_secret" ];Then ElementsRequest.send() calls _apply_auth for each scheme:
_self._apply_auth(_header, _params, security[_i], where);_apply_auth does:
case "auth_bearer":
var _token = _elements_request_auth_get_token("auth_bearer");
if (is_undefined(_token)) { /* log missing */ break; }
_header[? "Authorization"] = "Bearer " + _token;
break;
case "session_secret":
var _token = _elements_request_auth_get_token("session_secret");
if (is_undefined(_token)) { /* log missing */ break; }
_header[? "Elements-SessionSecret"] = _token;
break;So:
- If you don’t set a token, that scheme is simply skipped (no header added).
- If you do set it, it will be applied automatically on all endpoints that list that scheme.
From your example:
elements_create_username_password_session(_session_request, function(_code, _data, _request) {
if (_code == 200) {
obj_game.session_data = _data;
var _session_secret = _data.sessionSecret;
_elements_request_auth_set_token("auth_bearer", _session_secret);
// (optionally) also:
// _elements_request_auth_set_token("session_secret", _session_secret);
}
show_debug_message("elements_create_username_password_session :: " + string(_data));
});Pattern:
-
Call a login/auth endpoint.
-
In its callback:
- Extract token(s) from
_data. - Store them using
_elements_request_auth_set_token.
- Extract token(s) from
-
After that, all other endpoints needing those schemes will automatically send the right headers.
That’s the main thing a user of this API needs to understand about auth.
Here’s the lifecycle:
-
You call an
elements_*function. -
It creates an
ElementsRequestand calls.send(). -
send():- Builds headers and URL.
- Calls
http_request(...), gets an HTTP id. - Stores itself in
obj_elements_core.requests[? id] = self.
-
Later, GameMaker fires an Async HTTP event on
obj_elements_corewithasync_load.
Inside that event (your snippet):
var _async_id = async_load[? "id"];
var _request = requests[? _async_id];
if (_request == undefined) exit; // unknown request
var _status = async_load[? "status"];
if (_status == 1) exit; // still in progress
var _code = async_load[? "http_status"];
var _data = async_load[? "result"];Before your callback is run, hooks are checked:
var _hook = response_hooks[? _code];
if (is_callable(_hook) && _hook(_code, _data, _request) == true) {
ds_map_delete(requests, _async_id);
return;
}- A hook is a global handler you can register per HTTP status code.
- If it returns
true, it “consumes” the response and the per-request callback is skipped.
You register a hook like:
_elements_request_response_set_hook(401, function(_code, _data, _request) {
// global unauthorized handler, maybe redirect to login screen
// return true to stop further handling
return true;
});If no hook stops it, your callback is called:
var _callback = _request.get_callback();
if (is_callable(_callback)) {
try {
_data = json_parse(_data);
} catch(_ex) { /* if not JSON, leave _data as string */ }
_callback(_code, _data, _request);
}
ds_map_delete(requests, _async_id);So:
-
_codeis the HTTP status (200, 400, 500, …). -
_datais:- Parsed JSON → struct/array when possible.
- Raw string if
json_parsefails.
-
_requestis theElementsRequestinstance (you can inspect method, URL and even retry the request).
Your callbacks should always be ready to handle both success and error codes.
Every request passes a _content_type string into ElementsRequest:
-
Some endpoints fix it internally:
static _content_type = "application/json"; ... _elements_create_request(_url, _params, "POST", _body, _content_type, ...);
-
Some let you override it:
function elements_update_product_bundle_for_application_configuration(..., _body = undefined, _content_type = "*/*", _callback = undefined) { ... return _elements_create_request(_url, undefined, "PUT", _body, _content_type, _security, _callback, _GMFUNCTION_); }
Internally, when an ElementsRequest is created:
if (!is_undefined(_body)) {
_body = _process_body(_body, _content_type, _where);
if (!is_string(_body)) {
__.body_type = 1; // buffer
__.body = buffer_base64_encode(_body, 0, -1);
} else {
__.body = _body;
}
}So:
-
_content_typecontrols:- The
Content-Typeheader. - Which converter function is used to turn your struct/array into a wire format.
- The
Converters are registered globally:
_elements_request_body_set_converter("application/json", function(value) {
return json_encode(value);
});At send time, _process_body does:
var _body_converter = _elements_request_body_get_converter(_content_type);
if (!is_callable(_body_converter)) {
show_error(_where + " :: No converter for '" + _content_type + "'.", true);
}
_body = _body_converter(_body); // must be string or bufferThis means:
-
For application/json:
- You’re expected to have a converter that
json_encodes your struct/array.
- You’re expected to have a converter that
-
For other content types:
- You can define your own converters (e.g. multipart, binary, etc.).
-
If you pass a content type that has no converter registered:
- The request hard-errors (
show_error).
- The request hard-errors (
For the product bundle endpoint:
var bundles = [ /* array of ElementsProductBundle structs */ ];
// Let’s say your converter expects JSON when content_type is "application/json"
elements_update_product_bundle_for_application_configuration(
"my-app",
"my-config",
bundles,
"application/json",
function(_code, _data, _request) { ... }
);If you pass "*/*" and no converter is registered for "*/*", you’ll get an error. So in practice, you nearly always want "application/json" unless you’ve set up something custom.
The ElementsXXX constructors (ElementsItem, ElementsUser, ElementsRewardIssuance, etc.) are data containers. Each of them has a corresponding ElementsXXX_validate that can be used for validation.
Example:
var _user = new ElementsUser("user-id", "USER", "username");
var _item = new ElementsItem("sword", "Sword of Testing", "A test sword.", "FUNGIBLE");
var _issuance = new ElementsRewardIssuance(
"reward-id",
_user,
"ISSUED",
"my-context",
"NON_PERSISTENT",
_item,
10
);
// Before sending:
ElementsRewardIssuance_validate(_issuance, _GMFUNCTION_); // endpoints do this for youKey points:
-
ElementsXXX_validate()checks all fields and willshow_errorif something is wrong (wrong type, missing required fields).
The user of the API mostly needs to know:
- You don’t need to call
ElementsXXX_validate()yourself – the endpoint does it – but you can call it early to fail fast.
ElementsRequest tracks how many times it was sent:
attempts = 0;
static retry = function() { return send(); }- The
send()method incrementsattemptseach time it’s called. - If you keep a reference to
_request(e.g. via_requestin your callback), you can manually call_request.retry()to resend it. - This is useful if you want a per-request retry after a specific error, rather than a global hook.
-
Login / acquire tokens
elements_create_username_password_session(_session_request, function(_code, _data, _request) { if (_code == 200) { var _session_secret = _data.sessionSecret; _elements_request_auth_set_token("auth_bearer", _session_secret); _elements_request_auth_set_token("session_secret", _session_secret); } });
-
Optionally set global response hooks
_elements_request_response_set_hook(401, function(_code, _data, _request) { show_debug_message("Unauthorized, redirecting to login..."); // change room, clear saved session, whatever return true; // don’t call the per-request callback });
-
Call other endpoints normally
elements_get_application_profiles("my-app", 0, 20, "", function(_code, _data, _request) { if (_code == 200) { // use _data (already JSON-parsed) } else { // handle error } });
-
When sending bodies, use the right struct types + content_type
var _receipt = new ElementsCreateAppleIapReceipt(receipt_base64, "PRODUCTION"); elements_upload_apple_iap_receipt(_receipt, function(_code, _data, _request) { // ... });
GameMaker 2026
- Application
- Auth
- Auth Scheme
- Blockchain
- Codegen
- Deployment
- Elements
- Elm
- Followee
- Follower
- Friend
- Health
- Index
- Inventory
- Invite
- Ios
- Item
- Large Object
- Large Object Mp
- Leaderboard
- Meta
- Metadata
- Metadata Spec
- Mission
- Mock Session
- Multi Match
- Notification
- Oidc
- Product
- Profile
- Progress
- Rank
- Receipt
- Reward Issuance
- Save Data
- Schedule
- Score
- Session
- Signup
- Steam
- User
- Verify
- Version