Integrations

Connect the tools your team already uses. Everything is per app, under Settings → Apps → your app → Integrations in the inbox. Only a signed-in admin can connect, change or remove an integration: API keys can't, so a leaked key can't reroute your players' messages or read your credentials. An API key gets 403 human_approval_required, even with scope all. The API examples below use an admin's session cookie. Secrets are encrypted at rest and never returned by the API.

Every URL below uses https://app.inngame.app. URLs you already pasted elsewhere with the old inngame.meffysto.workers.dev address keep working.

Telegram

Answer players from a Telegram group. Each player gets a topic with their profile; you reply inside it.

  1. Create a bot with @BotFather and copy its token.
  2. Create a group for your team and turn on Topics (recommended: one topic per player). Add the bot as an admin so it can create topics.
  3. In Inngame, paste the bot token and pick the group: Inngame lists the groups the bot was added to (or write something in the group first), or you can enter the chat id by hand (for example -1001234567890). Inngame sets up the webhook and posts a confirmation in the group. Channels are not supported.

Without topics, players' messages arrive as notifications and you answer by replying to them. In both cases:

curl -X PUT https://app.inngame.app/v1/apps/$APP_ID/integrations/telegram \
  -b "ig_session=$INNGAME_SESSION" -H "Content-Type: application/json" \
  -d '{"botToken": "123456:ABC...", "chatId": "-1001234567890"}'
# botToken can be omitted later to keep the saved one

# Before connecting: list the groups the bot is in → {"groups": [{"id", "title", "topics"}], "botUsername"}
curl -X POST https://app.inngame.app/v1/apps/$APP_ID/integrations/telegram/groups \
  -b "ig_session=$INNGAME_SESSION" -H "Content-Type: application/json" -d '{"botToken": "123456:ABC..."}'

# Send a test notification
curl -X POST https://app.inngame.app/v1/apps/$APP_ID/integrations/telegram/test -b "ig_session=$INNGAME_SESSION"

Discord

One thread per player in a forum channel (recommended) or a text channel of your team's server. The channel must be private to your team: anything written in a player's thread is sent to that player as a support reply, so Inngame refuses a channel that @everyone, or any role without the Manage Messages permission, can see. A public community server works, with a private support channel inside it.

  1. Either invite the Inngame bot with Add to Discord in Settings → Apps → your app → Integrations → Discord (the install links that server to your app, so no other studio can connect the shared bot to it), or use your own bot: create an application in the Discord developer portal, enable the Message Content intent, and set its Interactions Endpoint URL to https://app.inngame.app/hooks/discord/<appId>/interactions.
  2. Copy the channel id (Developer Mode → right-click the channel → Copy Channel ID) and connect. With your own bot, also give its token, application id and public key.

Inngame registers the slash commands on your server and posts a confirmation. Write in a player's thread to answer (text or images); this turns the AI off for that conversation. Commands in a player's thread: /close, /reopen, /ai, /draft, /noai, /review, /block; anywhere: /stats, /pause, /resume. Buttons, drafts and echoed replies work like on Telegram, and bug spike alerts and the weekly digest are posted in the channel. Commands and buttons only work for members with the Manage Messages permission (or Administrator, Manage Server, Manage Threads) or a linked Inngame account (members: {"<discord user id>": "<inngame user id>"}, which also signs their replies); you can open the commands to another role in Discord's Server Settings → Integrations → your bot.

curl -X PUT https://app.inngame.app/v1/apps/$APP_ID/integrations/discord \
  -b "ig_session=$INNGAME_SESSION" -H "Content-Type: application/json" \
  -d '{"channelId": "112233445566778899"}'
# own bot: add "botToken", "applicationId", "publicKey"

Steam

Needed for verified Steam identity. Inngame checks each ticket with Steam's ISteamUserAuth/AuthenticateUserTicket.

  1. In Steamworks, create a publisher Web API key (Users & Permissions → Manage Groups → your group → Web API key).
  2. In Inngame, enter your Steam App ID and that key.
curl -X PUT https://app.inngame.app/v1/apps/$APP_ID/integrations/steam \
  -b "ig_session=$INNGAME_SESSION" -H "Content-Type: application/json" \
  -d '{"appId": "480", "webApiKey": "0123456789ABCDEF0123456789ABCDEF"}'
# webApiKey can be omitted later to keep the saved one

For Steam review cards, set the Steam App ID in the app settings too (settings.reviews.steamAppId) or in the SDK options.

GitHub

Turns grouped bugs into issues, and asks a coding agent for a fix.

  1. Create a fine-grained personal access token limited to the game's repository, with Issues: read and write.
  2. Connect the repository (owner/name) and the token.
  3. To get fix PRs, install a coding agent on the repository that answers mentions, such as the Claude Code GitHub Action (@claude). Use fixMention if yours answers to another handle.

Issues are labelled bug and inngame and include the summary, the number of players affected, app versions, platforms and log excerpts, never the players' identity. Request fix (request_fix in MCP) opens the issue if needed, then comments on it mentioning fixMention (default @claude), asking for a minimal fix and a pull request, and explicitly not to merge. When you ship, set fixedInVersion on the bug and Inngame tells every reporter once they run that version.

All of this is on the Bugs page of the inbox: players affected over time, versions, platforms and devices, the last error from the logs, the linked conversations, and buttons to open the issue, mark the bug fixed in a version (with how many players already run it), close, reopen or merge duplicates. Each bug has a link to that page, https://app.inngame.app/bugs/<bugId>?org=<orgId> (org switches the inbox to the right studio).

Spike alerts: when 5 players report the same bug within 24 hours, Inngame alerts the team once per bug: in the inbox, in Telegram and Discord (with a button to the bug page), and with a bug.spike webhook.

curl -X PUT https://app.inngame.app/v1/apps/$APP_ID/integrations/github \
  -b "ig_session=$INNGAME_SESSION" -H "Content-Type: application/json" \
  -d '{"repo": "lantern-games/lantern-quest", "token": "github_pat_...", "fixMention": "@claude"}'
# token can be omitted later to keep the saved one

# With an API key: open the issue for a bug (scope reply), ask for a fix (scope all), mark it fixed (scope reply)
curl -X POST https://app.inngame.app/v1/bugs/$BUG_ID/github-issue -H "Authorization: Bearer $INNGAME_API_KEY"
curl -X POST https://app.inngame.app/v1/bugs/$BUG_ID/request-fix -H "Authorization: Bearer $INNGAME_API_KEY"
curl -X PATCH https://app.inngame.app/v1/bugs/$BUG_ID -H "Authorization: Bearer $INNGAME_API_KEY" \
  -H "Content-Type: application/json" -d '{"fixedInVersion": "1.4.2"}'

Stripe and Google Play refunds

Let admins refund from the conversation. Only a signed-in admin can refund; agents and API keys can only propose a refund, which an admin then approves (rules). Each refund adds a note to the conversation and fires a refund.issued webhook.

# Admin session only. provider: stripe {paymentId, amountCents?}, googleplay {orderId, revoke?}, apple {transactionId}; optional "message" to the player
curl -X POST https://app.inngame.app/v1/conversations/$CONV_ID/refund \
  -b "ig_session=$INNGAME_SESSION" -H "Content-Type: application/json" \
  -d '{"provider": "stripe", "paymentId": "pi_3Q...", "amountCents": 499}'

# Agents and API keys (scope reply): propose, a human admin approves
curl -X POST https://app.inngame.app/v1/conversations/$CONV_ID/refund-proposal \
  -H "Authorization: Bearer $INNGAME_API_KEY" -H "Content-Type: application/json" \
  -d '{"provider": "stripe", "reference": "pi_3Q...", "amountCents": 499, "reason": "Charged twice"}'

Stripe

Use a restricted key (rk_live_…, or rk_test_… to try it) with write access to Refunds only, or a secret key. Admins then refund a pi_… or ch_… payment, fully or partially.

curl -X PUT https://app.inngame.app/v1/apps/$APP_ID/integrations/stripe_payments \
  -b "ig_session=$INNGAME_SESSION" -H "Content-Type: application/json" \
  -d '{"secretKey": "rk_live_..."}'

Google Play

Create a Google Cloud service account, grant it access to your app in the Play Console (financial data and order management), and upload its JSON key with your package name. Admins refund by order id; the purchase is revoked too unless you pass "revoke": false. The same service account is used to read Google Play notifications.

curl -X PUT https://app.inngame.app/v1/apps/$APP_ID/integrations/googleplay \
  -b "ig_session=$INNGAME_SESSION" -H "Content-Type: application/json" \
  -d '{"packageName": "com.lanterngames.quest", "serviceAccountJson": "{...}"}'

Apple

Only Apple can refund App Store purchases. From a conversation, an admin sends the player Apple's official refund request sheet for a transaction id; Apple decides, usually within 48 hours. No setup needed.

Push: APNs and FCM

Inngame sends a push when the team or the AI answers and the player doesn't have the chat open. Tokens Apple or Google report as dead are removed.

APNs (iOS)

Create an APNs auth key (.p8) in your Apple Developer account (Certificates, Identifiers & Profiles → Keys). Upload it with its Key ID, your Team ID and the app's bundle id. The same key works for development (apns_sandbox) and production tokens.

curl -X PUT https://app.inngame.app/v1/apps/$APP_ID/integrations/apns \
  -b "ig_session=$INNGAME_SESSION" -H "Content-Type: application/json" \
  -d '{"keyId": "ABC123DEFG", "teamId": "XYZ987WVUT", "bundleId": "com.lanterngames.quest", "p8": "-----BEGIN PRIVATE KEY-----\n..."}'

FCM (Android)

In the Firebase console, Project settings → Service accounts → Generate new private key, then upload the JSON.

curl -X PUT https://app.inngame.app/v1/apps/$APP_ID/integrations/fcm \
  -b "ig_session=$INNGAME_SESSION" -H "Content-Type: application/json" \
  -d '{"serviceAccountJson": "{...}"}'

Then pass the device token from the game: Unity, iOS.

Push for your team

Your team can get a notification on their phone or computer when a player writes, even with the inbox closed. Each person turns it on for their own device in Settings → Notifications → Enable push on this device. Nothing to configure for the studio.

RevenueCat

Subscription events become automation triggers, and each player's profile shows their subscription status, plan and renewal date (metadata subscription_status, plan, renews_on). The same applies to all the subscription sources below.

  1. Connect RevenueCat in Inngame. You get a webhook URL and an Authorization header value.
  2. In RevenueCat, Integrations → Webhooks: paste the URL and the authorization header.
  3. Tell RevenueCat who the player is: log in with Inngame's purchase token (Purchases.logIn(Inngame.PurchaseToken)), or with your own account id if you identify players with a JWT or Steam. A plain unverified id is never trusted with a subscription.
curl -X PUT https://app.inngame.app/v1/apps/$APP_ID/subscriptions/revenuecat -b "ig_session=$INNGAME_SESSION"
# → {"webhookUrl": "https://app.inngame.app/hooks/revenuecat/app_...", "authorizationHeader": "Bearer ..."}
# Calling it again returns the same URL and secret.
RevenueCat eventInngame event
INITIAL_PURCHASEtrial_started or subscription_started
RENEWALtrial_converted or subscription_renewed
CANCELLATIONtrial_cancelled, subscription_cancelled, or refunded (customer support cancellations)
BILLING_ISSUEbilling_issue
EXPIRATIONtrial_expired or subscription_expired
UNCANCELLATIONsubscription_renewed
NON_RENEWING_PURCHASEsubscription_started

Use these as the trigger of an automation: for example, on billing_issue, wait an hour, then send a message with a link to update the payment method. The ten subscription events are trial_started, trial_converted, trial_cancelled, trial_expired, subscription_started, subscription_renewed, subscription_cancelled, billing_issue, subscription_expired and refunded.

App Store notifications

Without RevenueCat, Inngame can read Apple's App Store Server Notifications (version 2) directly. Each notification is a JWS signed by Apple; Inngame checks the certificate chain up to Apple Root CA - G3 and rejects anything else, even on the secret URL.

  1. Settings → Apps → your app → Integrations → App Store notifications → Connect, then copy the webhook URL.
  2. In App Store Connect: your app → App Information → App Store Server Notifications. Paste the URL for production and sandbox, version 2.
  3. When the player buys, pass Inngame's purchase token as StoreKit's appAccountToken. Inngame issues it per player, so nobody can claim someone else's purchases.
guard let token = Inngame.purchaseToken else { return }   // a UUID, ready after the first session
let result = try await product.purchase(options: [.appAccountToken(token)])
curl -X PUT https://app.inngame.app/v1/apps/$APP_ID/subscriptions/appstore -b "ig_session=$INNGAME_SESSION"
# → {"webhookUrl": "https://app.inngame.app/hooks/appstore/app_...?s=..."}

Mapping: SUBSCRIBED → subscription_started (or trial_started for a free trial), DID_RENEW → subscription_renewed, DID_CHANGE_RENEWAL_STATUS with auto-renew turned off → subscription_cancelled (or trial_cancelled) and turned back on → subscription_renewed, DID_FAIL_TO_RENEW → billing_issue, EXPIRED and GRACE_PERIOD_EXPIRED → subscription_expired (or trial_expired), REFUND → refunded. Apple's test notification is accepted.

Google Play notifications

Google Play's real-time developer notifications arrive through Pub/Sub. They don't say who paid, so Inngame reads the subscription back from Google with the service account you connected for Google Play refunds; the pushed content itself is never trusted.

  1. Connect Google Play (package name and service account) if not done yet.
  2. Settings → Apps → your app → Integrations → Google Play notifications → Connect, then copy the webhook URL.
  3. In Google Cloud, create a Pub/Sub topic, grant google-play-developer-notifications@system.gserviceaccount.com the Publisher role, and add a push subscription to the webhook URL. Select the topic in Play Console → Monetization setup.
  4. At purchase, call setObfuscatedAccountId(Inngame.PurchaseToken) on the billing flow params. Inngame also checks that the notification matches the subscription's current state at Google.
curl -X PUT https://app.inngame.app/v1/apps/$APP_ID/subscriptions/googleplay -b "ig_session=$INNGAME_SESSION"
# → {"webhookUrl": "https://app.inngame.app/hooks/googleplay/app_...?s=..."}

Mapping: PURCHASED → subscription_started, RENEWED/RECOVERED/RESTARTED → subscription_renewed, CANCELED → subscription_cancelled, ON_HOLD/IN_GRACE_PERIOD → billing_issue, REVOKED → refunded, EXPIRED → subscription_expired.

Adapty

Settings → Apps → your app → Integrations → Adapty → Connect gives a webhook URL and an authorization header value. In Adapty → Integrations → Webhooks, paste both for production (and sandbox if you want test purchases). Inngame answers Adapty's verification request. Players are matched by customer_user_id: use Inngame's purchase token (Adapty.identify(Inngame.PurchaseToken)), or your own account id if players are verified with a JWT or Steam.

curl -X PUT https://app.inngame.app/v1/apps/$APP_ID/subscriptions/adapty -b "ig_session=$INNGAME_SESSION"
# → {"webhookUrl": "https://app.inngame.app/hooks/adapty/app_...", "authorizationHeader": "Bearer ..."}

Mapping (Adapty's default event names): trial_started, trial_converted, trial_expired keep their names; trial_renewal_cancelled → trial_cancelled; subscription_started and subscription_initial_purchase → subscription_started; subscription_renewed, subscription_renewal_reactivated and trial_renewal_reactivated → subscription_renewed; subscription_renewal_cancelled → subscription_cancelled; billing_issue_detected and entered_grace_period → billing_issue; subscription_expired → subscription_expired; subscription_refunded → refunded.

Superwall

Superwall signs each webhook (v2) with a secret it shows once. In Inngame, Settings → Apps → your app → Integrations → Superwall shows the webhook URL, https://app.inngame.app/hooks/superwall/<appId>; create the webhook in Superwall (Integrations → Webhooks) with it, then paste the signing secret (whsec_…) back into Inngame. Requests with a bad signature, or older than 5 minutes, are rejected. Players are matched by app_user_id and its aliases: use Inngame's purchase token as Superwall's user id, or your own account id if players are verified with a JWT or Steam.

curl -X PUT https://app.inngame.app/v1/apps/$APP_ID/subscriptions/superwall \
  -b "ig_session=$INNGAME_SESSION" -H "Content-Type: application/json" -d '{"signingSecret": "whsec_..."}'
# → {"webhookUrl": "https://app.inngame.app/hooks/superwall/app_..."}

Mapping: initial_purchase → subscription_started (or trial_started), non_renewing_purchase → subscription_started, renewal → subscription_renewed (or trial_converted), uncancellation and subscription_extended → subscription_renewed, cancellation → subscription_cancelled (or trial_cancelled), billing_issue → billing_issue, expiration → subscription_expired (or trial_expired), refund → refunded.

Webhooks

Send Inngame events to your own server: grant claimed items, log refunds, page someone on a bug spike. Webhooks belong to the studio, optionally limited to one app (appId), and are managed in Settings → Webhooks or with the API, by a signed-in admin only: API keys get 403 human_approval_required. The secret (whsec_…) is returned once, at creation.

# Create (https only; events defaults to ["*"]) → {"id": "wh_...", "secret": "whsec_..."}
curl -X POST https://app.inngame.app/v1/webhooks \
  -b "ig_session=$INNGAME_SESSION" -H "Content-Type: application/json" \
  -d '{"url": "https://api.lanterngames.com/inngame", "appId": "app_...", "events": ["item.claimed", "refund.issued"]}'

# List (with last_status, last_error, pending retries, and the list of events) · send a ping · delete
curl https://app.inngame.app/v1/webhooks -b "ig_session=$INNGAME_SESSION"
curl -X POST https://app.inngame.app/v1/webhooks/$WEBHOOK_ID/test -b "ig_session=$INNGAME_SESSION"
curl -X DELETE https://app.inngame.app/v1/webhooks/$WEBHOOK_ID -b "ig_session=$INNGAME_SESSION"

Each delivery is a POST of {"id": "evt_…", "type", "created" (ms), "appId", "data"}, signed in the inngame-signature header: t=<unix seconds>,v1=<HMAC-SHA256 of "t.body" with your secret, base64url>. Answer 2xx within 8 seconds; network errors, 408, 429 and 5xx are retried for about a day with the same event id. Details and verification code: REST API & webhooks.

EventWhendata
message.createdA message is sent in a conversation (player, team or AI; not drafts or notes).conversationId, playerId, message
item.claimedA player claims an item card (once per card).messageId, conversationId, player, card, action
card.actionA player taps, dismisses or fails to open any other card.Same as item.claimed.
refund.issuedAn admin issued a refund or sent the Apple refund sheet.conversationId, playerId, provider, id, status
bug.spike5 players reported the same bug within 24 hours (once per bug).bugId, title, reports24h, url
conversation.closedA conversation is closed (inbox, API, Telegram or Discord).conversationId, playerId, by (inbox and API only)
player.blockedA player is blocked (inbox, API, Telegram or Discord).playerId, by (inbox and API only)
message.retractedThe team unsent a message.conversationId, messageId, playerId, by
conversation.ratedA player rated a closed conversation (satisfaction on).conversationId, messageId, rating (1 to 5), player
digest.weeklyThe weekly digest. Studio-wide (appId is null), so only webhooks not limited to an app receive it.apps[], suggestions[]
pingSent by the test endpoint, to that webhook only.hello

Weekly digest

Every Monday from 08:00 UTC, Inngame sends the past week of support for each app with activity: conversations, player messages, team and AI replies, average first reply time, satisfaction, players waiting more than 24 hours, AI handoffs, top tags and top bugs (with links to their page), plus up to three suggestions. It goes to every admin by email, to each app's connected Telegram group and Discord channel (with a button to the stats), and to your webhooks as digest.weekly. Nothing is sent after a week without any activity. Nothing to configure.

Pause or disconnect

Anyone with read access, API keys included, can list the integrations (without secrets); only a signed-in admin can pause, resume or disconnect them. The kind in the path is one of telegram, discord, steam, github, stripe_payments, googleplay (refunds), apns, fcm, revenuecat, appstore, googleplay_rtdn (Google Play notifications), adapty, superwall. A paused subscription source rejects its webhooks. Disconnecting Telegram removes the bot's webhook; disconnecting Discord frees the server for another studio.

# List (secrets are never returned)
curl https://app.inngame.app/v1/apps/$APP_ID/integrations -b "ig_session=$INNGAME_SESSION"

# Pause / resume
curl -X PATCH https://app.inngame.app/v1/apps/$APP_ID/integrations/telegram \
  -b "ig_session=$INNGAME_SESSION" -H "Content-Type: application/json" -d '{"enabled": false}'

# Disconnect
curl -X DELETE https://app.inngame.app/v1/apps/$APP_ID/integrations/telegram -b "ig_session=$INNGAME_SESSION"