Skip to content

C++ examples — Ludex ingestion

Game servers and native tools often call Ludex over HTTPS with a small JSON payload. Below: a libcurl example you can drop into a server build, plus notes for Unreal Engine (FHttpModule).

Paths and JSON match the live API: POST /v1/ingest/event, POST /v1/ingest/batch, Authorization: Bearer, Content-Type: application/json. Success is HTTP 202.


Prerequisites (libcurl)

  • Link libcurl and ensure TLS backend is configured for https:// in production.
  • Build your JSON string however you prefer (std::format, manual concatenation, nlohmann/json, RapidJSON, etc.).

libcurl — single event

Replace string literals with your real project_id, environment (must match the API key’s environment id), and API_KEY.

#include <curl/curl.h>
#include <string>
#include <iostream>

static size_t write_cb(char* ptr, size_t size, size_t nmemb, void* userdata) {
    auto* out = static_cast<std::string*>(userdata);
    out->append(ptr, size * nmemb);
    return size * nmemb;
}

int main() {
    const char* base_url = "https://ingest.ludexstudio.com/v1/ingest/event";
    const char* api_key = "YOUR_API_KEY";

    // Minimal valid body: ISO-8601 timestamp, event_name, properties object.
    const std::string json = R"({
  "project_id": "YOUR_PROJECT_ID",
  "environment": "YOUR_ENVIRONMENT_ID",
  "event": {
    "event_name": "server_match_started",
    "timestamp": "2026-04-13T12:00:00.000Z",
    "session_id": "sess-native-1",
    "properties": { "map": "arena_01" },
    "sdk": { "name": "cpp-libcurl-example", "version": "1.0.0" }
  }
})";

    CURL* curl = curl_easy_init();
    if (!curl) return 1;

    std::string response_body;
    struct curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, "Content-Type: application/json");
    std::string auth_hdr = std::string("Authorization: Bearer ") + api_key;
    headers = curl_slist_append(headers, auth_hdr.c_str());
    headers = curl_slist_append(headers, "Idempotency-Key: cpp-demo-001");
    headers = curl_slist_append(headers, "X-Correlation-Id: corr-cpp-001");

    curl_easy_setopt(curl, CURLOPT_URL, base_url);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast<long>(json.size()));
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_body);

    CURLcode res = curl_easy_perform(curl);
    long http_code = 0;
    if (res == CURLE_OK)
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);

    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    std::cout << "http=" << http_code << "\n" << response_body << "\n";
    return (res != CURLE_OK || http_code != 202) ? 1 : 0;
}

Compile (example flags vary by platform):

c++ -std=c++17 ingest_example.cpp -lcurl -o ingest_example

libcurl — batch

Same pattern: POST {base}/v1/ingest/batch with body:

{
  "project_id": "YOUR_PROJECT_ID",
  "environment": "YOUR_ENVIRONMENT_ID",
  "events": [
    {
      "event_name": "tick",
      "timestamp": "2026-04-13T12:00:01.000Z",
      "properties": { "tps": 60 }
    }
  ]
}

Requires scope ingest:batch. Default max 5000 events per request.


Retries

On 429 or 5xx, sleep with exponential backoff and retry the same logical event with the same event_id (if you include one). Do not blindly retry 401, 403, 422, or 413.


Unreal Engine (FHttpModule)

For game clients, Unreal’s FHttpModule::Get().CreateRequest() is the usual transport:

  1. SetURL(FString::Printf(TEXT("%s/v1/ingest/event"), *BaseUrl))
  2. SetVerb(TEXT("POST"))
  3. SetHeader(TEXT("Authorization"), FString::Printf(TEXT("Bearer %s"), *ApiKey))
  4. SetHeader(TEXT("Content-Type"), TEXT("application/json"))
  5. Optional: Idempotency-Key, X-Correlation-Id, X-Request-Id
  6. SetContentAsString(JsonBody) then ProcessRequest()

Reuse the same JSON field names as in contract-reference.md. A first-party Ludex Unreal SDK is planned (not shipped in this repo yet); for the pilot, keep transport in your game or server code with the same /v1/ingest/* contract, retries, and idempotency rules as in the C++ retries section above.