cURL
Integrate the Referral Kit with no SDK — sign and POST events from the shell with openssl + curl, including a reusable Bash helper and full worked examples.
No SDK, no language runtime — just curl and openssl. This is the bare-metal
path: useful for testing, cron jobs, shell-based game tooling, or proving the
contract before you wire a "real" integration. Any language that can compute an
HMAC and make an HTTPS POST can do the same thing.
Prefer a packaged path? The official SDKs wrap this exact contract
for Node, PHP, Python, and .NET — npm i @mmolove/referral,
pip install mmolove-referral, dotnet add package MMOLove.Referral, or a
single-file PHP drop-in.
What you need
- Your signing secret and server id from the Referrals tab.
curlandopensslon the box (both ubiquitous).
SECRET='your-server-secret' # from the Referrals tab — keep it out of shell history
SERVER_ID='<your-server-id>' # your MMOLove server id
ENDPOINT='https://mmolove.com/api/referral/events'Don't paste a real secret into an interactive shell (it lands in ~/.bash_history).
Read it from an env var or a file: SECRET="$(cat /run/secrets/mmolove)".
The signing recipe
The MAC is HMAC_SHA256(secret, "<t>.<rawBody>") in lower-case hex. Build the body
once in $BODY, sign exactly that, and send exactly that.
T=$(date +%s)
BODY='{"event":"registered","token":"<mmref>","server_id":"'"$SERVER_ID"'","referee_identity":"<player>","server_event_id":"reg-<player>","ts":'"$T"'}'
# HMAC-SHA256 over "<T>.<BODY>", hex, stripping openssl's "(stdin)= " prefix.
MAC=$(printf '%s' "$T.$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')
curl -i -X POST "$ENDPOINT" \
-H 'Content-Type: application/json' \
-H "X-MMOLove-Signature: t=$T,v1=sha256=$MAC" \
-d "$BODY"printf '%s' (not echo) avoids a trailing newline — that newline would change
the signed bytes and you'd get a 401. And -d "$BODY" sends the same string
you signed. Don't let your shell re-quote or reformat it in between.
A reusable helper
mmolove-send.sh — sign + POST any event. Pass the body as $1.
#!/usr/bin/env bash
set -euo pipefail
SECRET="${MMOLOVE_REFERRAL_SECRET:?set MMOLOVE_REFERRAL_SECRET}"
ENDPOINT='https://mmolove.com/api/referral/events'
# Usage: mmolove-send.sh '<json-body-without-ts>'
# We inject a fresh ts + sign + POST.
mmolove_send() {
local body_in="$1"
local t; t=$(date +%s)
# Inject ts into the body (assumes a trailing '}' to splice before).
local body="${body_in%\}},\"ts\":$t}"
local mac; mac=$(printf '%s' "$t.$body" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')
curl -sS -o /tmp/mmolove_resp -w '%{http_code}' -X POST "$ENDPOINT" \
-H 'Content-Type: application/json' \
-H "X-MMOLove-Signature: t=$t,v1=sha256=$mac" \
-d "$body"
echo # newline after the status code
cat /tmp/mmolove_resp; echo
}
mmolove_send "$1"1. Capture the mmref token
There's nothing to call here. MMOLove delivers the token in the browser:
https://your-server.example/register?mmref=<token>Read mmref from the query string (or the mmref cookie) on your registration
page and store it against the new account. Echo it back as token in the events
below.
2. Report registered — mint the anchor
T=$(date +%s)
BODY='{"event":"registered","token":"<mmref>","server_id":"'"$SERVER_ID"'","referee_identity":"<player>","server_event_id":"reg-<player>","ts":'"$T"'}'
MAC=$(printf '%s' "$T.$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')
curl -i -X POST "$ENDPOINT" \
-H 'Content-Type: application/json' \
-H "X-MMOLove-Signature: t=$T,v1=sha256=$MAC" \
-d "$BODY"
# Expect: 200 {"ok":true,"referral_id":"...","state":"registered"}referee_identity is required here (the anchor key) and should be a stable
id, not a renamable name.
3. Report qualified at your milestone
Same token, a new server_event_id:
T=$(date +%s)
BODY='{"event":"qualified","token":"<mmref>","server_id":"'"$SERVER_ID"'","server_event_id":"qual-<player>","ts":'"$T"'}'
MAC=$(printf '%s' "$T.$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')
curl -i -X POST "$ENDPOINT" \
-H 'Content-Type: application/json' \
-H "X-MMOLove-Signature: t=$T,v1=sha256=$MAC" \
-d "$BODY"
# Expect: 200 {"ok":true,"referral_id":"...","state":"qualified"}Then grant the reward in-game.
Test it first (dry run)
Add "test":true — fully signature-verified, never written:
T=$(date +%s)
BODY='{"event":"registered","token":"<mmref>","server_id":"'"$SERVER_ID"'","referee_identity":"<player>","server_event_id":"test-1","ts":'"$T"',"test":true}'
MAC=$(printf '%s' "$T.$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')
curl -i -X POST "$ENDPOINT" \
-H 'Content-Type: application/json' \
-H "X-MMOLove-Signature: t=$T,v1=sha256=$MAC" \
-d "$BODY"
# Expect: 200 {"ok":true,"test":true}Reading the response
The -i flag prints the status line so you can branch on it:
| Status | Meaning | Do |
|---|---|---|
200 | Accepted (incl. duplicate / ignored:first_touch_conflict / test) | Stop retrying. On qualified, grant the reward. |
400 | Malformed | Fix the body/header — Errors. |
401 | Bad signature or stale t | The raw-body bug (often a stray newline), wrong/old secret, or clock drift. |
404 | Unknown server/token or referrals off | Check server_id, enable referrals, verify the token. |
422 | Invalid transition | Send registered before qualified. |
5xx | Server error | Retry with backoff (idempotent via server_event_id). |
If a hand-built curl 401s but your code path works (or vice-versa), it's nearly
always a whitespace/quoting difference in the signed bytes. Print "$T.$BODY"
exactly as signed and compare it byte-for-byte to what you POST.
Next
.NET / C#
A complete, copy-paste .NET integration for the Referral Kit — HMACSHA256 + HttpClient, with signing, error handling, and a full ASP.NET example.
All languages at a glance
A concise side-by-side of the referral flow across PHP, Node/JS, Python, .NET/C#, and cURL — pick your stack, then jump to the full SDK guide.