Testing & self-verify
Prove your reward-callback wiring with the dashboard "Send test callback" button and the delivery log — without waiting for a real vote.
You can prove your callback end-to-end without waiting for a real player to vote. The dashboard fires a real, signed delivery on demand, and the delivery log shows you exactly what happened.
"Send test callback"
On your server's Integration tab, Send test callback delivers a signed
heart.test event to your
configured callback URL using your current secret — the same code path a real
heart.counted uses, just with placeholder values:
{
"event": "heart.test",
"server_id": "<your-server-id>",
"username": "test-user",
"heart_id": "00000000-0000-0000-0000-000000000000",
"period": "2026-06",
"timestamp": 1733500000
}It's signed exactly like a real callback (X-MMOLove-Signature: t=<unix>,v1=<hex>,
X-MMOLove-Event: heart.test), so a passing test proves your URL, your
secret, and your signature verification are all correct end-to-end.
The test callback is always sent as a signed POST — it ignores the legacy GET toggle. If you run a legacy GET integration, test it by triggering a real vote (or hand-build a GET against your verifier — see Legacy GET).
Reading the result
The dashboard reports the outcome inline:
| Result | Meaning |
|---|---|
Test callback delivered (HTTP 2xx) | Your endpoint received it and returned success. Wiring confirmed. |
Test failed: unsafe_url | The callback URL isn't a public https:// address — fix it and save. |
Test failed: not_configured | No callback URL or no secret yet — set both first. |
Test failed: request_failed | The request never completed (DNS, TLS, timeout, connection refused). Check the endpoint is reachable from the public internet. |
Test failed (HTTP 4xx/5xx) | Your endpoint was reached but returned an error — almost always your signature check rejecting it. Compare the bytes you verified against the raw body. |
A heart.test has the all-zero heart_id and the username test-user. Your
handler should verify-and-acknowledge it (return 2xx) but not actually pay
out — branch on event === "heart.test" (or the X-MMOLove-Event header).
Handling the test in your code
<?php
// After verifying the signature (see the PHP guide):
$body = json_decode($raw, true);
if (($body['event'] ?? '') === 'heart.test') {
http_response_code(200); // acknowledge — do NOT reward test-user
exit;
}
grant_reward($body['username'], $body['streak_day'] ?? null);
http_response_code(200);The delivery log
The Delivery log on the Integration tab is your window into every real
heart.counted delivery, newest first. Each row shows:
- a status chip —
ok(delivered, with the HTTP status code),fail(failed / exhausted), orretry(pending a retry), - the event (
heart.counted), - the attempt count (
1 try,3 tries, …), and - a relative time ("just now", "4 min ago", "2 h ago").
Use it to confirm:
- Did the delivery happen? A row appears for every counted heart.
- Did your endpoint accept it? An
okchip with a2xxcode means yes; afail/retrychip with a4xx/5xxmeans your endpoint rejected it (usually a signature mismatch) — fix it and the next vote will go through, or it'll be retried automatically. - Is it retrying? Multiple
trieson a row means earlier attempts didn't get a2xx. See the retry schedule.
A clean smoke test
Configure + secret
Set the callback URL, Rotate secret, and store the secret where your handler reads it.
Send test callback
Click it. Expect Test callback delivered (HTTP 2xx). If your endpoint 4xxs,
fix your signature verification now — a real vote would fail the same way.
Real vote
Cast a heart on your own listing (from a clean session). Watch a heart.counted
row appear in the delivery log with an ok chip.
Idempotency
Confirm your grant is keyed on heart_id so a retried delivery (same heart_id)
is a no-op. See Payload → idempotency.
See also
Legacy GET mode
The older GET-with-query-params reward callback for legacy vote scripts — its URL shape, the username|ts signature, and how to verify it.
Errors, retries & delivery
How MMOLove delivers reward callbacks — the retry schedule, delivery statuses, what counts as success, and how to troubleshoot failures.