For Developers

Vote rewards API, ready code for Java and PTS servers

Players vote much more often when they get something in game for it. Connect vote rewards to your Java or PTS server in 10–15 minutes: your project climbs the monthly TOP, and players get items for every vote.

Quick start

1

Get Server ID and API key

Your server must be listed on L2Servers.top. Write to us — we send the ID, the secret key and your vote page link.

Contact us
2

Install the reward script

One PHP file for your website — Java and PTS in the same script. It receives our signed request after every vote and gives items to the character.

⬇ l2s-vote-reward.php
3

Test and go live

Send a test vote with the tool below, then put the vote button on your website and launcher. We mark your server with 🎁 on the vote page.

Open test tool
Vote pagehttps://l2servers.top/vote/{server}/
Limit1 vote / 12 h per IP and character
SignatureHMAC-SHA256
Ranking reset1st day of month

Java servers: L2J, L2J Mobius, aCis, L2Scripts, Lucera

Two ways — use one or both:

A. Automatic reward (callback) — recommended

  1. Upload l2s-vote-reward.php to your website (PHP 7.2+ with pdo_mysql, access to the game database).
  2. Set server_type = java, the database and the reward mode:
    • custom_mail — L2J Mobius: items come by in-game mail, online or offline (General.ini → CustomMailManagerEnabled = True).
    • items_delayed — L2Scripts, Lucera, Rebellion, Overworld: the item is given on the next login.
    • sql — aCis and custom packs: your own INSERT/UPDATE (for example vote points in your donate shop).
  3. Open ?test=1&char=YourChar on your script — it checks the connection, tables and character lookup.
  4. Send us the callback URL:
https://your-site.com/l2s-vote-reward.php?user={username}&ip={ip}&time={time}&sig={sig}
'api_key' => 'YOUR_API_KEY', 'server_type' => 'java', 'db_dsn' => 'mysql:host=127.0.0.1;port=3306;dbname=l2jgs;charset=utf8mb4', 'db_user' => 'l2j', 'db_pass' => 'secret', 'mode' => 'custom_mail', 'items' => array( array( 57, 1000000 ), array( 1538, 1 ) ), // Adena + Blessed Scroll of Escape

B. In-game command .vote

The player votes on our site and types .vote in game. The server asks our API whether this character (or IP) voted in the last 12 hours and gives the reward once per vote. Works without a website — only the game server is needed.

⬇ L2ServersVote.java (L2J Mobius)

For aCis / L2Scripts / Lucera keep the same logic and change three things: the voiced command interface, the "add item" call and where the last claimed vote is stored.

PTS servers (C++, MSSQL)

On PTS you usually cannot add a Java-style command, and writing items directly into user_item while the character is online is unsafe. That is why the reward script works with PTS through the database — it finds the character in lin2world.dbo.user_data and hands the reward to a system that your server already uses:

1. Extender item delivery Most PTS extenders have an item delivery table — the item comes to the character on login or by a command. Put its INSERT into custom_sql (the table name depends on your extender).
2. Points on your website Credit vote points to the account in your donate shop / cabinet (:account placeholder) — players spend them there. Needs no changes on the game server.
3. Manual (log) mode = log: the script only writes who voted to l2s-votes.log, and a GM gives rewards manually. Good for the first days.

PTS config example (PHP needs pdo_sqlsrv on Windows or pdo_dblib on Linux):

'api_key' => 'YOUR_API_KEY', 'server_type' => 'pts', 'db_dsn' => 'sqlsrv:Server=127.0.0.1,1433;Database=lin2world', 'db_user' => 'sa', 'db_pass' => 'secret', 'mode' => 'sql', 'items' => array( array( 57, 1000000 ) ), // 1) extender delivery table (check the name in your extender docs): 'custom_sql' => 'INSERT INTO ItemDelivery (char_id, item_id, item_amount) VALUES (:char_id, :item_id, :count)', // 2) or vote points on the website (separate DB connection): // 'custom_sql' => 'UPDATE site_accounts SET vote_points = vote_points + :count WHERE login = :account', // 'sql_dsn' => 'mysql:host=127.0.0.1;dbname=website;charset=utf8mb4',

PTS and the .vote command: if your extender supports custom commands or scripts, it can call the same API as the Java example (/api/vote-check?username=…) — the logic is identical.

API reference

Callback (we call you)

After every accepted vote we send one GET request to your callback URL (timeout 3 s, no retries). Placeholders:

PlaceholderValue
{username}Character name entered by the player (letters, digits, _ and -; may be empty)
{ip}Voter IP address
{time}Unix time of the vote (UTC)
{sig}hex( HMAC-SHA256( username + "|" + ip + "|" + time, API_KEY ) )

Verify it yourself (the ready script already does this and rejects old and repeated requests):

// PHP $expected = hash_hmac('sha256', $_GET['user'] . '|' . $_GET['ip'] . '|' . $_GET['time'], $apiKey); if (!hash_equals($expected, $_GET['sig']) || abs(time() - (int) $_GET['time']) > 600) { http_response_code(403); exit; } // Java Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(apiKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); String expected = HexFormat.of().formatHex(mac.doFinal((user + "|" + ip + "|" + time).getBytes(StandardCharsets.UTF_8)));

Vote check (you call us)

GET https://l2servers.top/api/vote-check?server=SERVER_ID&key=API_KEY&username=CharName GET https://l2servers.top/api/vote-check?server=SERVER_ID&key=API_KEY&ip=1.2.3.4
HTTPResponse
200{"voted":true,"voted_at":"2026-09-25T10:15:00+00:00","next_vote_at":"2026-09-25T22:15:00+00:00"}
200{"voted":false,"voted_at":null,"next_vote_at":null}
400{"error":"bad_request","message":"Pass username or ip."}
403{"error":"invalid_key","message":"Invalid server ID or API key."}

Store the last voted_at you rewarded for the account, so one vote gives exactly one reward. Keep the API key secret — use it only on your server, never in the game client or JavaScript.

Test tool

Check your key, see if a character has voted, and send a real signed test request to your script — no need to vote yourself.

The test callback is a real signed request: if the character exists, your script will give the reward — use a test character.

Vote button for your website

Vote for us on L2Servers.top

<a href="https://l2servers.top/vote/YOUR-SERVER/" target="_blank"><img src="https://l2servers.top/vote-button.svg" width="88" height="31" alt="Vote for us on L2Servers.top"></a>

Replace YOUR-SERVER with your address on L2Servers.top (for example /vote/forceplay/). Add ?username=Name to prefill the character name — useful in the launcher and in the website account page.

FAQ

Is it free?

Yes. Votes, the API and the reward script are free for every listed server.

The callback does not arrive

Use a normal domain with https:// (443) or http:// (80), no IP addresses or custom ports, and answer within 3 seconds. Check with the test tool — it shows the HTTP status and the first 300 characters of your answer.

"expired" in the answer

The clock on your web server is wrong by more than 10 minutes. Enable time sync (NTP).

Can players cheat with many characters?

No: one vote per 12 hours per IP address and per character name, plus bot protection. For extra safety give the reward once per account (the Java example does this).

Can I change the key?

Yes — write to us and we generate a new one; the old key stops working immediately.