Linkzly

Nintendo Switch SDK Setup Guide (C++ / NX SDK)

---

9 min read

sidebar_label: Nintendo Switch SDK (C++ / NX SDK) sidebar_category: Gaming SDK Guides

Nintendo Switch SDK Setup Guide (C++ / NX SDK)

This guide covers integrating Gaming Intelligence into Nintendo Switch games using C++ and the NX SDK. It demonstrates the HTTP transport layer, event serialization, HMAC request signing, and offline queue patterns that are compatible with Nintendo's platform requirements.

Developer Program Note: Nintendo Switch SDK access requires enrollment in the Nintendo Developer Program and acceptance of the associated NDA. The NX SDK headers, libraries, and toolchain are available exclusively through Nintendo's developer portal. This guide assumes you already have a licensed NX SDK environment configured.


Prerequisites

Before you begin, have the following ready:

Requirement Where to Find It
SDK Key Linkzly Console → Gaming → [Your Game] → Settings → SDK Configuration → SDK Key
Organization ID Linkzly Console → Organization Settings
Game ID Linkzly Console → Gaming → [Your Game] → Settings → General Settings
Signing Secret Linkzly Console → Gaming → [Your Game] → Settings → SDK Configuration → Signing Secret
NX SDK Nintendo Developer Portal (requires Nintendo Developer Program license)
mbedTLS Included with or linkable in the NX SDK environment; used for HMAC-SHA256 signing
rapidjson or equivalent Lightweight JSON serializer for constructing event payloads

Your SDK key is masked by default in the console. Use the eye icon to reveal it or the copy button to copy it directly.


Platform Value

All events sent from Nintendo Switch must include "platform": "switch" in the event payload.


Architecture Overview

Because Nintendo Switch certification rules prohibit blocking the main thread on network I/O, the recommended integration pattern separates event production from event delivery:

  1. Game thread: Enqueues events into a thread-safe in-memory buffer.
  2. Network thread: Dequeues events in batches, serializes them, signs the request, and POSTs to the ingestion endpoint.
  3. Offline queue: If the console is offline or the request fails, events are persisted to save data and flushed on the next successful connection.

HTTP Transport

The NX SDK provides nn::http for making HTTP/HTTPS requests. Initialize the HTTP library once at startup and reuse it for the lifetime of your integration.

#include <nn/http.h>
#include <nn/nn_Common.h>

// Initialize once at startup (e.g., in your network module init)
nn::http::Initialize();

Sending an Event Batch

#include <nn/http.h>
#include <string>

void SendEventBatch(
    const std::string& body,
    const std::string& sdkKey,
    const std::string& orgId,
    const std::string& gameId,
    const std::string& signature,
    const std::string& timestamp,
    const std::string& nonce)
{
    nn::http::Request request;

    request.SetMethod(nn::http::MethodType_Post);
    request.SetUrl("https://gaming.linkzly.com/api/v1/gaming/events");

    request.AddHeader("Authorization",      ("Bearer " + sdkKey).c_str());
    request.AddHeader("X-Organization-ID",  orgId.c_str());
    request.AddHeader("X-Game-ID",          gameId.c_str());
    request.AddHeader("Content-Type",       "application/json");
    request.AddHeader("X-Signature-256",    signature.c_str());
    request.AddHeader("X-Timestamp",        timestamp.c_str());
    request.AddHeader("X-Nonce",            nonce.c_str());

    request.SetPostData(body.c_str(), body.size());

    nn::http::Response response = request.Send();

    int statusCode = response.GetStatusCode();
    if (statusCode == 202) {
        // Batch accepted — parse response body for batch_id, events_received, etc.
    } else {
        // Handle error; re-queue events for retry if appropriate
    }
}

Thread safety: nn::http::Request and nn::http::Response objects are not thread-safe. Create them on the network thread only.


JSON Serialization

Use rapidjson or a similarly lightweight serializer to construct the event payload. Avoid STL-heavy solutions that may increase binary size beyond certification limits.

#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"

std::string BuildEventPayload(
    const std::string& eventId,
    const std::string& eventType,
    const std::string& timestamp,
    const std::string& playerId,
    const std::string& sessionId,
    const std::string& gameVersion)
{
    using namespace rapidjson;

    Document doc;
    doc.SetObject();
    auto& alloc = doc.GetAllocator();

    // Build single event object
    Value event(kObjectType);
    event.AddMember("event_id",    Value(eventId.c_str(),    alloc), alloc);
    event.AddMember("event_type",  Value(eventType.c_str(),  alloc), alloc);
    event.AddMember("timestamp",   Value(timestamp.c_str(),  alloc), alloc);
    event.AddMember("platform",    "switch",                         alloc);
    event.AddMember("player_id",   Value(playerId.c_str(),   alloc), alloc);
    event.AddMember("session_id",  Value(sessionId.c_str(),  alloc), alloc);
    event.AddMember("game_version",Value(gameVersion.c_str(),alloc), alloc);

    Value events(kArrayType);
    events.PushBack(event, alloc);
    doc.AddMember("events", events, alloc);

    StringBuffer buffer;
    Writer<StringBuffer> writer(buffer);
    doc.Accept(writer);

    return std::string(buffer.GetString(), buffer.GetSize());
}

Player Identification

Nintendo Switch does not expose a persistent hardware device ID for privacy reasons. Use one of the following approaches for the player_id field:

  • Nintendo Account ID: If your game uses Nintendo Account login via nn::account, use the local account UID as the player identifier. This is a 128-bit value; serialize it as a hex string.
  • Locally generated UUID: If your game does not require Nintendo Account login, generate a UUID on first launch, persist it to save data, and use it as the player ID for the lifetime of that save file.
// Example: serialize nn::account::Uid as hex string
#include <nn/account.h>
#include <iomanip>
#include <sstream>

std::string AccountUidToString(const nn::account::Uid& uid)
{
    std::ostringstream oss;
    for (int i = 0; i < nn::account::Uid::Size; ++i) {
        oss << std::hex << std::setw(2) << std::setfill('0')
            << static_cast<int>(uid.id[i]);
    }
    return oss.str();
}

Session Management

Use nn::oe application focus events to detect when the game gains and loses focus. This maps directly to session start and session end:

#include <nn/oe.h>

void PollApplicationFocus()
{
    nn::oe::FocusState focusState;
    nn::oe::GetCurrentFocusState(&focusState);

    if (focusState == nn::oe::FocusState_InFocus) {
        // App has gained focus — start or resume session
        OnSessionStart();
    } else {
        // App has lost focus (home button, sleep, notification overlay)
        // End session and flush queued events to save data
        OnSessionEnd();
        FlushEventsToDisk();
    }
}

Assign a new UUID to session_id each time OnSessionStart() is called. Persist the current session ID so it survives brief focus interruptions if your game design requires it.


HMAC Request Signing

Use mbedTLS (available in the NX SDK build environment) to compute HMAC-SHA256 signatures. HMAC signing is enabled by default for all new games.

The signing string format is: {timestamp}.{nonce}.{body}

#include "mbedtls/md.h"
#include <cstring>
#include <iomanip>
#include <sstream>

std::string ComputeHmacSha256(
    const std::string& secret,
    const std::string& message)
{
    unsigned char hmac[32];
    const mbedtls_md_info_t* mdInfo =
        mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);

    mbedtls_md_hmac(
        mdInfo,
        reinterpret_cast<const unsigned char*>(secret.data()),
        secret.size(),
        reinterpret_cast<const unsigned char*>(message.data()),
        message.size(),
        hmac);

    std::ostringstream oss;
    for (int i = 0; i < 32; ++i) {
        oss << std::hex << std::setw(2) << std::setfill('0')
            << static_cast<int>(hmac[i]);
    }
    return oss.str();
}

std::string BuildSigningString(
    const std::string& timestamp,
    const std::string& nonce,
    const std::string& body)
{
    return timestamp + "." + nonce + "." + body;
}

The timestamp must be a Unix epoch string (seconds). The nonce should be a randomly generated string, unique per request. The signing window is 300 seconds — requests with timestamps more than 5 minutes from the server clock are rejected.


Offline Queue

Nintendo Switch titles can run without network connectivity (handheld mode on an airplane, for example). Persist queued events to save data when offline so they are not lost.

// Pseudocode — adapt to your save data layer (nn::fs)

void EnqueueEvent(const EventRecord& event)
{
    inMemoryQueue.push_back(event);

    if (!IsNetworkAvailable()) {
        PersistQueueToSaveData(inMemoryQueue);
    }
}

void OnNetworkAvailable()
{
    // Load any events persisted to save data and merge with in-memory queue
    std::vector<EventRecord> persisted = LoadQueueFromSaveData();
    inMemoryQueue.insert(inMemoryQueue.begin(), persisted.begin(), persisted.end());
    ClearSaveDataQueue();

    // Flush all queued events
    FlushEventBatch();
}

Use nn::nifm to detect network availability changes and trigger OnNetworkAvailable() when the console reconnects.


Nintendo Certification Considerations

Nintendo's Lot Check requirements place constraints that affect how you integrate network functionality:

  • Do not block the main thread. All HTTP calls must run on a dedicated network thread. Use a producer-consumer queue to pass events from game code to the network thread.
  • Respect focus transitions. Flush and pause network activity when the application loses focus (nn::oe::FocusState_OutOfFocus). Resume when focus is regained.
  • Save data consistency. If you write events to save data for offline queuing, ensure save data writes are atomic. Corrupted save data can fail certification.
  • Data volume. Keep per-event payload sizes reasonable. The ingestion API enforces a 10 KB per-event limit and a 512 KB per-batch limit.

Required Event Fields

Every event sent to the ingestion endpoint must include these fields:

Field Type Description
event_id string Unique ID for this event (UUID recommended)
event_type string Event name (e.g., level_complete, purchase)
timestamp string ISO 8601 UTC timestamp (e.g., 2025-04-15T12:00:00Z)
platform string Must be "switch"
player_id string Stable identifier for the player
session_id string Identifier for the current play session

Ingestion Endpoint

POST https://gaming.linkzly.com/api/v1/gaming/events

Required headers:

Header Value
Authorization Bearer <SDK_KEY>
X-Organization-ID Your organization ID
X-Game-ID Your game ID
Content-Type application/json
X-Signature-256 HMAC-SHA256 hex digest
X-Timestamp Unix timestamp (seconds)
X-Nonce Random per-request string

Successful response (HTTP 202):

{
  "batch_id": "batch_01jk...",
  "events_received": 10,
  "events_valid": 10,
  "events_dropped": 0,
  "trace_id": "trace_abc..."
}

Troubleshooting

Events are not appearing in the Linkzly dashboard

  1. Verify sdkKey, orgId, and gameId are correctly set with no extra whitespace.
  2. Confirm the device has network connectivity (nn::nifm::IsNetworkAvailable()).
  3. Check HTTP response status codes and log the response body for error details.
  4. Ensure nn::http::Initialize() was called before any requests are made.

HMAC errors (HTTP 401)

  1. Confirm the signingSecret matches the value shown in Game Settings → SDK Configuration.
  2. Verify the console's system clock is correct. The replay window is 300 seconds.
  3. Confirm the signing string is constructed as {timestamp}.{nonce}.{body} with no extra characters.

Events dropped (events_dropped > 0)

Events are dropped by the server if: the per-event payload exceeds 10 KB, the timestamp is more than 24 hours in the past, or the timestamp is more than 5 minutes in the future. Log dropped events with their payloads to diagnose the cause.

Build errors with mbedTLS

Confirm that MBEDTLS_MD_C and MBEDTLS_SHA256_C are enabled in your mbedTLS configuration header. Some NX SDK distributions ship a minimal mbedTLS build that may require enabling these features explicitly.

Was this helpful?

Help us improve our documentation