Nintendo Switch SDK Setup Guide (C++ / NX SDK)
---
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:
- Game thread: Enqueues events into a thread-safe in-memory buffer.
- Network thread: Dequeues events in batches, serializes them, signs the request, and POSTs to the ingestion endpoint.
- 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::Requestandnn::http::Responseobjects 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
- Verify
sdkKey,orgId, andgameIdare correctly set with no extra whitespace. - Confirm the device has network connectivity (
nn::nifm::IsNetworkAvailable()). - Check HTTP response status codes and log the response body for error details.
- Ensure
nn::http::Initialize()was called before any requests are made.
HMAC errors (HTTP 401)
- Confirm the
signingSecretmatches the value shown in Game Settings → SDK Configuration. - Verify the console's system clock is correct. The replay window is 300 seconds.
- 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