Getting Started
This walkthrough takes you from a fresh dashboard signup to a working license check in the language of your choice.
If you have not read the Introduction, do that first. This page assumes you already know what an application, a license key, and an end user mean in Obsidian terms.
Prerequisites
Obsidian is Windows only. All three SDKs collect HWID through the Windows registry and WMI, and the shipped binaries target Windows 10 or 11 on x64.
You need a dashboard account at https://obsidianauth.com and a language toolchain that matches the SDK you plan to use: .NET Framework 4.7.2 or later for C#, MSVC 2019 or later for C++, or CPython 3.12 x64 for Python.
The SDKs pin the server's SPKI SHA-256 (4e93dd1f9ef0f20f84b93cac3bed8e279309fd07756e7835806f1711995c78ac). Any proxy that terminates TLS between you and obsidianauth.com will trip the pin, and every call will fail.
Step 1: Create an account
Go to https://obsidianauth.com and sign up. The first account created for a workspace becomes its owner, and owners can invite Admin, Developer, and Reseller members later from the Team page.
Turn on two-factor authentication the moment you land in the dashboard. This account holds your app IDs, HMAC secrets, and license keys, so a stolen password is a full workspace compromise.
Step 2: Create an application
An application is a single product you protect. Each one gets its own API key, HMAC secret, and end-user database.
- Open Applications in the sidebar.
- Click New Application.
- Give it a name, a version string such as
1.0, and decide whether HWID lock is on. - Save.
HWID lock ties each license to the first machine that activates it. See HWID Lock for how the HWID is computed and how a lock is reset.
Step 3: Get your credentials
Click into the new application and open the SDK Integration tab. Three values live there: api_key, api_secret, and base_url.
The api_key identifies your app in the /init handshake, and every subsequent call authenticates with the session token that handshake returns. The api_secret is the HMAC-SHA256 signing key, and it never leaves the compiled SDK binary you ship. The base_url is always https://obsidianauth.com in production.
If the secret ever leaks, hit Rotate secret on this tab. Rotation invalidates every in-flight signed request, so you need to push a client update alongside the rotation. The full playbook lives on the Security Model page.
Step 4: Generate a license key
Open Manage Keys from the sidebar.
- Click Generate Keys.
- Set the count, duration (for example
30d,1y,lifetime), and level. - Optionally pick a custom format like
XXXX-XXXX-XXXX-XXXX; leave it blank for opaque tokens. - Click Generate.
The new keys appear in the table with status unused. Grab one; you will hand it to an end user in the next step.
Step 5: Download the SDK
Back on the application page, the SDK Integration tab has three download buttons: C#, C++, and Python. Each ZIP is compiled for your specific app, so the api_key, api_secret, and version are baked into the SDK binary and the Example file.
The C# ZIP ships Obsidian.dll and BouncyCastle.Cryptography.dll (Ed25519 verification lives in BouncyCastle) plus Example.cs. The C++ ZIP ships obsidian_x64.dll and obsidian_x86.dll next to matching import libs, the public obsidian_client.h, and example.cpp. The Python ZIP ships obsidian_sdk.cp312-win_amd64.pyd with an example.py, and expects requests and cryptography on the target Python.
Do not share a ZIP across applications. The api_key and api_secret are compiled into the binary, so installing the wrong ZIP will authenticate as the wrong product.
Deeper per-language reference lives on the C# SDK, C++ SDK, and Python SDK pages.
Step 6: First integration call
The C# and Python SDKs run the /init handshake automatically on the first authenticated call. C++ is a manual C API, so you call obsidian_init_library() and obsidian_init_v2() yourself and verify every proof.
C#
using ObsidianSDK;
try
{
var sdk = new Obsidian(
"YOUR_API_KEY",
"YOUR_API_SECRET",
"1.0"
);
sdk.Register("someUser", "somePass", "PASTE-LICENSE-KEY-HERE");
Console.WriteLine("HWID: " + sdk.Hwid);
Console.WriteLine("Session: " + sdk.Session);
}
catch (ObsidianException ex)
{
Console.WriteLine("Auth failed: " + ex.Message);
}
The constructor takes three positional strings and nothing else. Session and Hwid are read-only properties (Hwid populated at construction, Session populated after the first authenticated call), and Capture is a settable bool that toggles whether a screenshot is attached to activation events.
Python
from obsidian_sdk import Obsidian, ObsidianError
try:
sdk = Obsidian(
api_key="YOUR_API_KEY",
api_secret="YOUR_API_SECRET",
version="1.0",
)
sdk.register("someUser", "somePass", "PASTE-LICENSE-KEY-HERE")
print("HWID:", sdk.hwid)
print("Session:", sdk.session)
print("User:", sdk.user)
except ObsidianError as e:
print("Auth failed:", e)
The Python constructor accepts api_key, api_secret, and version as keyword arguments, plus an optional capture=True. As with C#, register calls login internally on success, so sdk.user is populated by the time the call returns.
C++
The C++ SDK is a manual C API. Every call fills an ObsidianCallProof you pass back to obsidian_verify_proof; do not trust a result until the proof verifies.
#include "obsidian_client.h"
#include <cstdio>
int main()
{
ObsidianConfig Cfg = {
"YOUR_API_KEY",
"YourAppName",
"YOUR_API_SECRET",
"1.0",
nullptr,
};
obsidian_init_library();
char Session[256] = {0};
char Err[512] = {0};
ObsidianCallProof Proof = {0};
if (obsidian_init_v2(&Cfg, Session, sizeof Session, Err, sizeof Err, &Proof) != 0)
{
std::printf("init failed: %s\n", Err);
return 1;
}
if (!obsidian_verify_proof(&Proof, &Cfg, Session, sizeof Session))
{
std::printf("init proof failed (tampered response)\n");
return 1;
}
char Hwid[64] = {0};
uint32_t Sources = 0;
obs_compute_hwid(Hwid, sizeof Hwid, &Sources);
ObsidianAuthResult Result = {0};
if (obsidian_license_v2(&Cfg, Session, "PASTE-LICENSE-KEY-HERE",
Hwid, &Result, Err, sizeof Err, &Proof) != 0)
{
std::printf("license failed: %s\n", Err);
return 1;
}
if (!obsidian_verify_proof(&Proof, &Cfg, &Result, sizeof Result))
{
std::printf("license proof failed (tampered response)\n");
return 1;
}
std::printf("user=%s level=%d expires=%s\n",
Result.username, Result.level, Result.expires_at);
obsidian_shutdown_library();
return 0;
}
Ship obsidian_x64.dll (or the x86 variant) next to your .exe. The DLL is statically linked against the CRT, so no other runtime dependencies need to travel with it.
Step 7: Verify it worked
Open the Users panel for the application. The end user you just registered should be there with last_login populated, because register auto-logs in on success.
Open Manage Keys. The license key you used should have flipped from unused to used, with the HWID of the machine that ran the code recorded next to it.
If neither is true, the SDK raised something. Match the exception string against the table below, and cross-reference Client Endpoints for the full error catalogue.
| Symptom | Likely cause |
|---|---|
Invalid license key | The key belongs to a different application, or was banned. |
HWID mismatch. Key locked to another device. | The license has HWID lock on and was first activated on another machine. Reset it in Manage Keys. |
Application disabled | Someone toggled the app off. Re-enable it on the application detail page. |
server certificate pin mismatch (MITM or reissued cert) | A TLS-terminating proxy sits between the SDK and obsidianauth.com. This literal comes from the Python SDK; C# surfaces pin failures as a WebException, and C++ as an init-time error. |
invalid server signature (response forged or tampered) | Something between the SDK and the server rewrote the response body. |