Site data
With the data API, Qorum asks your site for people's names and cards (photo, badges, colors). Comments always show each person's current look, without your site having to send anything.
What it's for
- Always up-to-date card: the person changed their photo or got a badge on your site, and within 30 minutes the comments show it.
- People who haven't logged in for a while: old comments, or comments imported from another system, get a name and photo even if the person never came in through Qorum.
- Simpler token: the login token can carry just
subandname. Qorum fetches the rest.
If you also send card in the token, it's used the moment the person logs in. The data API keeps it up to date after that.
How Qorum calls it
When a list of comments has someone with no card, or with a card older than 30 minutes, Qorum calls your API in the background. The page doesn't wait for it: the person sees the card that already exists, and the new one shows up on the next reads. Each person is requested at most once every 10 minutes, and requests go in batches of up to 100.
POST https://mysite.com/qorum/users
Content-Type: application/json
User-Agent: Qorum-Dados/1
X-Qorum-Event: users.lookup
X-Qorum-Timestamp: 1794171698
X-Qorum-Signature: sha256=...
{ "ids": ["123", "77", "15"], "site": "my-blog" }
The signature is the same as for webhooks: HMAC-SHA256 with the webhook secret over timestamp + "." + body. Always check it: without it, anyone can list your users' data.
What to answer
Status 200 with the users you found. Leave out anyone who doesn't exist.
{
"users": {
"123": { "name": "Ana Lima", "card": { "username": "ana_lima", "displayName": "Ana Lima", "avatar": "https://...",
"rank": { "name": "Subscriber", "nameColor": "#7c3aed" } } },
"77": { "name": "Bruno", "card": { "username": "bruno", "avatar": "https://..." } }
}
}
The card follows the same rules as the login card: a free-form object of up to 8 KB. The keys in users are the same ids your site sends in the token's sub.
Answer within 8 seconds. If your API fails, Qorum keeps using the card it has and tries again after 10 minutes.
Turn it on
- Create the route on your site
See the examples below.
- Enter the address
In the dashboard under Settings → Your site's data API, or with
PUT /v1/site/settingsand{"config": {"apiDados": "https://..."}}. - Test it
In Test connection → Data API, enter the id of a real user. The test shows what your site answered and already saves the card. Through the API:
POST /v1/site/data-api/testwith{"userId": "123"}.
Examples
const crypto = require('crypto');
app.post('/qorum/users', express.raw({ type: 'application/json' }), async (req, res) => {
// same check as the webhook
const ts = req.get('X-Qorum-Timestamp') || '';
const expected = 'sha256=' + crypto.createHmac('sha256', process.env.QORUM_WEBHOOK_SECRET)
.update(ts + '.').update(req.body).digest('hex');
const received = req.get('X-Qorum-Signature') || '';
if (received.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))
|| Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.sendStatus(401);
const { ids } = JSON.parse(req.body);
const found = await db.users.findMany({ where: { id: { in: ids.map(Number).filter(Boolean) } } });
const users = {};
for (const u of found) {
users[String(u.id)] = {
name: u.name,
card: { username: u.username, displayName: u.name, avatar: u.photoUrl, isStaff: u.admin },
};
}
res.json({ users });
});
<?php
$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_QORUM_TIMESTAMP'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, getenv('QORUM_WEBHOOK_SECRET'));
if (!hash_equals($expected, $_SERVER['HTTP_X_QORUM_SIGNATURE'] ?? '') || abs(time() - (int) $ts) > 300) {
http_response_code(401); exit;
}
$ids = array_filter(array_map('intval', json_decode($body, true)['ids'] ?? []));
$users = [];
if ($ids) {
$marks = implode(',', array_fill(0, count($ids), '?'));
$q = $pdo->prepare("SELECT id, name, username, photo_url, admin FROM users WHERE id IN ($marks)");
$q->execute(array_values($ids));
foreach ($q as $u) {
$users[(string) $u['id']] = [
'name' => $u['name'],
'card' => ['username' => $u['username'], 'displayName' => $u['name'], 'avatar' => $u['photo_url'], 'isStaff' => (bool) $u['admin']],
];
}
}
header('Content-Type: application/json');
echo json_encode(['users' => (object) $users]);
Search API (links with #)
The commenter types # and the name of something on your site (a work, a product, a post). Qorum asks your site which pages match and shows a list. The chosen one goes into the text as [Name](https://your-site/page) and becomes a clickable link. With no result, the person can paste an outside link.
POST https://mysite.com/qorum/search
X-Qorum-Event: links.search
X-Qorum-Timestamp: 1794171698
X-Qorum-Signature: sha256=... (same signature as webhooks)
{ "q": "solo lev", "site": "my-blog", "lang": "en" }
// answer 200: up to 8 results
{ "results": [
{ "title": "Solo Leveling", "url": "https://mysite.com/work/solo-leveling",
"image": "https://mysite.com/covers/solo.webp", "subtitle": "Manhwa" }
] }
Answer within 4 seconds. Qorum keeps each search for 5 minutes. Turn it on by entering the address in Settings → Your site's search API (or {"config": {"apiBusca": "https://..."}}) and test it in Test connection → Search API.
When the change has to show up right away
The data API updates within 30 minutes. If someone bought something and needs to see the new look immediately, tell Qorum right after the purchase with PUT /v1/site/users/:userId/card in the management API. For many people at once (a migration, a new badge for everyone), use PUT /v1/site/users/cards, with up to 1000 people per request in the same format as this answer.