PokeIndie integrations

Three focused integration silos for automating release notes, connecting verified pokes to in-game rewards, and bringing configured games into your Discord community.

Integration 01

Changelog Hook

PokeIndie pulls a public JSON feed every 12 hours, deduplicates entries, stores them locally, and publishes them in the game’s year/month changelog archive.

# Game token

Open Dashboard → Games → Edit → Links and copy the public game token. It identifies the listing but is not a secret or permission to create pokes.

{"game":"7d889576-c166-40ea-8ee3-20f15c49f832"}

# Feed schema

Your endpoint may return one object or an array. game, date, and log are required. Title, version, and major status are optional.

[
  {
    "game": "7d889576-c166-40ea-8ee3-20f15c49f832",
    "date": "2026-07-20T18:30:00Z",
    "title": "The Forge Update",
    "version": "1.4.0",
    "major": true,
    "log": "Added co-op raids.\nRebalanced iron production.\nFixed save migration."
  }
]
  1. Host the JSON on a public HTTP or HTTPS URL.
  2. Paste it into Changelog JSON feed URL.
  3. Save the game listing.
  4. New entries appear after the next scheduled synchronization.

# Delivery, limits, and retries

  • Feeds run at 01:00 and 13:00 server time.
  • Each log is limited to 50,000 characters; the response is limited to 1 MB.
  • A mismatched game token rejects the feed without storing entries.
  • Date and log content form the deduplication fingerprint.
  • Private, loopback, and internal destinations are blocked.
php artisan changelogs:sync --game=7d889576-c166-40ea-8ee3-20f15c49f832
php artisan schedule:work

Integration 02

Poke Reward Hook

Send a player through a simple PokeIndie reward link, preserve their opaque game account ID through registration, and receive POST callbacks for website and Discord pokes.

# Create the player reward link

  1. Set an HTTPS Poke callback URL and save the game.
  2. Redirect the player to the game’s poke URL with your opaque player ID as user_id.
  3. PokeIndie stores the value in an encrypted, HTTP-only cookie for 180 days, so registration and login redirects do not lose it.
  4. When that browser account pokes the game, the POST callback contains the same player ID.
$gameSlug = 'your-game-slug';
$playerId = 'player_84920';
$url = 'https://pokeindie.com/games/'.$gameSlug.'/poke?'.http_build_query([
    'user_id' => $playerId,
]);

header('Location: '.$url);

The player attribution value is intentionally not authenticated by PokeIndie. Treat it only as an opaque identifier and decide on your own backend whether it is eligible for a reward. Never put email addresses, access tokens, or sensitive personal information in user_id.

# Callback payloads

Every successful poke source creates a durable event. Website pokes carry the attributed game user ID when available:

{
  "event_id": "682c6f40-202a-4af4-84db-72a69eb69f54",
  "event": "poke.created",
  "game": "7d889576-c166-40ea-8ee3-20f15c49f832",
  "game_slug": "your-game-slug",
  "poke_id": 9814,
  "source": "website",
  "count": 33142,
  "recipient": {
    "type": "developer_user",
    "user_id": "player_84920"
  },
  "occurred_at": "2026-07-20T18:42:10+00:00"
}

Discord cannot read the browser attribution cookie, so Discord events provide the member identity for your backend to map:

{
  "event_id": "46f688f8-b43c-461e-a9c5-65f7f7647aa2",
  "event": "poke.created",
  "game": "7d889576-c166-40ea-8ee3-20f15c49f832",
  "game_slug": "your-game-slug",
  "poke_id": 9815,
  "source": "discord",
  "count": 33143,
  "recipient": {
    "type": "discord",
    "discord_user_id": "345678901234567890",
    "discord_username": "playername",
    "discord_display_name": "Player Name"
  },
  "occurred_at": "2026-07-20T18:45:10+00:00"
}

Return any 2xx response after durably accepting the event. PokeIndie retries after 60 seconds and then 5 minutes, for at most three total attempts. Delivery status, response code, and the latest error appear below the game editor.

# Authenticate and process callbacks

Authenticate every callback cryptographically. PokeIndie sends X-PokeIndie-Event, X-PokeIndie-Timestamp, and X-PokeIndie-Signature. The signature is sha256=HMAC_SHA256(timestamp + "." + raw_request_body, signing_secret).

Do not authenticate callbacks using DNS or IP addresses. DNS records, reverse DNS, source-IP ranges, X-Forwarded-For, and informational origin headers can change or be misconfigured. Only a valid HMAC signature made with your game’s private callback secret proves that the payload is authentic.
$body = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_POKEINDIE_TIMESTAMP'] ?? '';
$provided = $_SERVER['HTTP_X_POKEINDIE_SIGNATURE'] ?? '';

if (abs(time() - (int) $timestamp) > 300) {
    http_response_code(401);
    exit('Stale callback');
}

$expected = 'sha256='.hash_hmac(
    'sha256',
    $timestamp.'.'.$body,
    $_ENV['POKEINDIE_CALLBACK_SECRET']
);

if (!hash_equals($expected, $provided)) {
    http_response_code(401);
    exit('Invalid signature');
}

$event = json_decode($body, true, flags: JSON_THROW_ON_ERROR);
// Insert event_id into a UNIQUE column before granting the reward.
// If it already exists, return 204 without rewarding twice.
http_response_code(204);
  • Use the raw body when verifying; parsing and re-encoding JSON changes the signature.
  • Reject timestamps older than five minutes.
  • Make event_id unique in your database so retries cannot grant duplicate rewards.
  • Map Discord users by immutable discord_user_id, not username.
  • Treat all recipient values as identifiers, never authorization credentials.
  • Run PokeIndie queue workers in production or callbacks remain pending.

Integration 03

Discord Bot

Connect one Discord server directly to one game. Community members run /poke with no game argument; PokeIndie resolves the game securely from the server.

# Requirements

  • An active PokeIndie Premium membership.
  • A Discord account linked from your PokeIndie profile.
  • Discord server ownership or the Manage Server permission.
  • At least one game owned by your PokeIndie account.
  • Community members must link their Discord identity to a PokeIndie account before using /poke.

# Installation

  1. Open the game in Dashboard → Games and select its Discord tab.
  2. Connect your Discord account if it is not linked.
  3. Choose one server where you are owner or have Manage Server.
  4. Approve the game-bound authorization screen. The state expires after 10 minutes and cannot be reused for a different game or server.
  5. Move the PokeIndie bot role above every role it may reward, then return to the Discord tab and refresh server data.
The bot needs View Channels, Send Messages, Use Application Commands, and Manage Roles. PokeIndie rejects managed, administrator, and higher roles even if Discord returns them.

# Game, channels, and rewards

  1. Choose all eligible channels or restrict /poke to selected channels.
  2. Select rich, compact, or private responses.
  3. Add up to four reward roles with monthly or lifetime poke thresholds.
  4. Save and run the safe test. It validates presence, freshness, channels, and role permissions without creating a poke.
{
  "interaction_id": "123456789012345678",
  "discord_guild_id": "123456789012345678",
  "discord_channel_id": "234567890123456789",
  "discord_user_id": "345678901234567890"
}

The official bot signs this private payload. The backend derives the game from the server, enforces channel and Premium state, prevents interaction replay, applies cooldowns, and records monthly statistics. Developers never send a game token to this endpoint.

# Slash commands

CommandStatusBehavior
/pokeAvailableRecords a poke for the linked member on the game connected to the current server, limited to once every 4 hours per member per game; there is no game option.
/game-info <game>AvailableLooks up any published PokeIndie listing by name, with autocomplete suggestions as you type. Not limited to the game connected to this server. Replies with the developer, short description, pricing model, release date, lifetime pokes, and listing link.
/game-statsAvailableShows pokes this month, monthly unique members, and lifetime pokes for the game connected to the current server; there is no game option.
/poke-leaderboardAvailableRanks the top members by pokes this month for the game connected to the current server, mentioning each member. Resets with the monthly period.

The bot refreshes server health and its global “pokes this month” status every five minutes. Role changes run asynchronously and retry temporary failures. /game-stats and /poke-leaderboard require the same active, Premium-connected server integration as /poke; /game-info works in any server the bot has joined.

# Troubleshooting

Premium integration is inactive

Renew Premium and allow up to 15 minutes for status synchronization. Moderators and administrators bypass this gate for support.

Link your Discord account first

Sign in with Discord or connect it from profile settings; the Discord member must map to a PokeIndie user.

This server requires reconfiguration

A legacy server was linked to zero or multiple games. Open the game Discord tab and explicitly reconnect it; history remains intact.

Command cannot complete in this channel

Choose all eligible channels or select this channel in the game Discord tab, then save.

A reward role is not assignable

Give the bot Manage Roles, move its role above the reward role, refresh server data, and reselect the role.

Poke request was replayed or rate limited

Discord interactions are idempotent and fresh creations have a cooldown. Wait for the indicated interval rather than resending the same interaction.