Base URL and Endpoint

Authentication

Provide the following HTTP headers on every request.

Request

Content-Type: application/json

{
  "userId": "[USER_TELEGRAM_ID]"
}

Payload and serialization

The payload is the exact raw JSON request body sent to the endpoint. Compute X-Signature over this exact string as UTF-8 bytes, and send the identical string as the HTTP body. Any change in whitespace, key order, or a trailing newline changes the signature.

C# example

using System;
using [System.Net](<http://System.Net>).Http;
using [System.Security](<http://System.Security>).Cryptography;
using System.Text;
using System.Text.Json;

static string ComputeSignatureBase64(string secret, string payload)
{
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
    var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
    return Convert.ToBase64String(hash);
}

// Build payload as a compact JSON string (no extra whitespace)
var payloadObj = new { userId = telegramUserId.ToString() };
var json = JsonSerializer.Serialize(payloadObj, new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    WriteIndented = false
});

var signature = ComputeSignatureBase64(partnerSecret, json);

using var client = new HttpClient();
using var req = new HttpRequestMessage([HttpMethod.Post](<http://HttpMethod.Post>), $"{{[<https://webhook.p1.rumblearcade.io/quests/{questId}>](<https://webhook.p1.rumblearcade.io/quests/{questId}>)}}")
{
    Content = new StringContent(json, Encoding.UTF8, "application/json")
};
req.Headers.Add("X-Partner-Id", partnerId);
req.Headers.Add("X-Signature", signature);

var resp = await client.SendAsync(req);
resp.EnsureSuccessStatusCode();

TypeScript/Node.js example

import crypto from 'crypto';
import fetch from 'node-fetch';

function computeSignatureBase64(secret: string, payload: string): string {
  const hmac = crypto.createHmac('sha256', Buffer.from(secret, 'utf8'));
  const hash = hmac.update(Buffer.from(payload, 'utf8')).digest();
  return Buffer.from(hash).toString('base64');
}

const payloadObj = { userId: String(telegramUserId) };
const body = JSON.stringify(payloadObj); // compact JSON

const signature = computeSignatureBase64(partnerSecret, body);

const res = await fetch(`{{[<https://webhook.p1.rumblearcade.io/quests/${questId}>}}`](<https://webhook.p1.rumblearcade.io/quests/${questId}>}}`), {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Partner-Id': partnerId,
    'X-Signature': signature,
  },
  body,
});

if (!res.ok) {
  throw new Error(`Request failed: ${res.status}`);
}

<aside> ⚠️

Use the identical string for both the signature and the HTTP body. Differences in whitespace or key order will produce a different signature.

</aside>