Subscription error reference

Every code every subscription route can return, grouped by route. All bodies are {"message": "...", "error_code": "..."} unless a different shape is shown. Where a body carries status too, branch on status first. Match on error_code, never on message.

Authentication and gates (every route)

HTTPCode or bodyMeaningRemedy
401{"message": "X-Game-Secret-Key header is required"}No key.Send the header.
401{"message": "Invalid game secret key"}Unknown or rotated-out key.Use the current key for this environment.
403{"message": "Game '...' is not active"}Title suspended.Contact Invo.
403GAME_NOT_LIVECreate refused; title is testing.Set the title live in the console, in sandbox or production alike.
403PAYMENTS_PAUSEDCreate or Steam init refused; INVO has paused payments on the account. Existing subscriptions keep renewing.Contact INVO support.
503{"status": "error", "error": "flow_paused", "message": "..."}Maintenance pause (create, amount, payment-method, Steam init, card setup).Retry later with the same idempotency key.
503FLOW_PAUSEDMaintenance pause (refund, sandbox clock).Retry later.
429{"error": "rate_limit_exceeded", "message": "...", "retry_after": n, "limit_type": "rate_limit"}Rate limited (every route with a per-minute or per-hour limit, including the card-setup mint).Honour Retry-After.
429RATE_LIMITEDRate limited (refund, sandbox clock).Slow down.
429{"error": "spending_limit_exceeded", "message": "..."}Member spending limit (Steam init).Retry later.
404SUBSCRIPTION_NOT_FOUNDNot your subscription, or no such id.Check the id and the key.
400INVALID_BODYBody is not a JSON object.Send an object.
400INVALID_FIELD_VALUEA value the database refused (length, characters).Fix the value.
500INTERNAL_ERRORUnexpected.Retry with the same idempotency key.

Create (/subscribe, /steam/init)

HTTPCodeMeaningRemedy
400CLIENT_REQUEST_ID_REQUIRED, CLIENT_REQUEST_ID_INVALID, CLIENT_REQUEST_ID_TOO_LONGMissing, not a string, or over 255 chars.Send a string of at most 255 chars.
400CLIENT_REQUEST_ID_RESERVEDKey begins with sub_.Do not begin keys with sub_.
400PLAYER_EMAIL_INVALID, PLAYER_EMAIL_TOO_LONG, PLAYER_NAME_REQUIRED, PLAYER_NAME_TOO_LONG, PLAYER_PHONE_TOO_LONGIdentity field invalid.Fix the field.
400ITEM_ID_REQUIRED, ITEM_ID_TOO_LONG, ITEM_NAME_TOO_LONGItem field invalid.Fix the field.
400AMOUNT_REQUIRED, AMOUNT_INVALID, AMOUNT_TOO_SMALL, AMOUNT_TOO_LARGEPrice invalid.0.01 to 999999.99; keep at or below the per-charge ceiling of 25000.00 to be chargeable.
400INTERVAL_INVALID, INTERVAL_COUNT_INVALID, INTERVAL_COUNT_OUT_OF_RANGEInterval invalid.month or year; count 1 to 36 (1 on Steam).
400TRIAL_DAYS_INVALID, TRIAL_DAYS_OUT_OF_RANGE, TRIAL_END_INVALID, TRIAL_END_IN_PAST, TRIAL_END_TOO_FARTrial invalid.1 to 365 days; ISO 8601 in the future.
400TRIAL_AMOUNT_REQUIRES_TRIALtrial_amount_usd sent without trial_days or trial_end.Send a trial length with it, or drop it for no trial.
400TRIAL_AMOUNT_NOT_SUPPORTED_FOR_FUNDINGtrial_amount_usd with wallet_only: true, or on /steam/init.Paid trials are card subscriptions only.
400TRIAL_AMOUNT_INVALIDtrial_amount_usd is not an amount, is below 0.50 (the card minimum), or is not less than amount_usd.Send a decimal string from 0.50 up to, but not including, amount_usd.
400TRIAL_AMOUNT_BELOW_FEE_FLOORA new paid trial whose trial_amount_usd would leave you nothing after Invo’s card fee on the trial charge. Nothing was created. The message names the smallest trial price accepted for your account. A subscription already created replays as before.Raise trial_amount_usd to at least the price the message names.
400PAYMENT_METHOD_REQUIREDA paid trial and the member has no usable saved card. Nothing was created. (On /payment-method: no card id sent.)Save the member’s card first, then subscribe.
400METADATA_INVALID, METADATA_TOO_LARGEMetadata invalid.JSON object, at most 8192 bytes, no NUL.
400CONSENT_AT_INVALID, CONSENT_INVALID, CONSENT_DISCLOSED_AMOUNT_INVALID, CONSENT_DISCLOSED_INTERVAL_INVALID, CONSENT_DISCLOSED_TRIAL_AMOUNT_INVALID, CONSENT_DISCLOSED_TRIAL_DAYS_INVALID, CONSENT_DISCLOSED_TRIAL_END_INVALIDConsent field invalid.Fix the consent field.
400REVENUE_SHARE_INVALID, REVENUE_SHARE_PERCENT_OUT_OF_RANGE, REVENUE_SHARE_RECIPIENT_REQUIRED, REVENUE_SHARE_RECIPIENT_IS_SUBSCRIBER, REVENUE_SHARE_RECIPIENT_NOT_FOUNDRevenue share invalid.Name an existing player in this title who is not the subscriber; percent 0 to 100.
400CURRENCY_NOT_CONFIGUREDThe title has no currency.Configure the title currency.
400PAYMENT_METHOD_INVALID, PAYMENT_METHOD_EXPIREDCard id invalid or card expired.Pass an integer card id of an unexpired card.
404PAYMENT_METHOD_NOT_FOUNDThe card is not this player’s in this title, or the player does not exist yet.Save a card for the player first.
409WALLET_ONLY_SUBSCRIPTIONwallet_only: true combined with a card, or sent on Steam.Do not combine wallet_only: true with a card; on Steam do not send wallet_only.
409ACTIVE_SUBSCRIPTION_EXISTS (+ subscription_id)The player already has a live subscription to this item, on either rail.Use the live subscription. Same item id across rails is intended.
409CONCURRENT_REQUESTTwo creates for a new player collided.Retry with the same key.
409CLIENT_REQUEST_ID_CONFLICTKey already used for a different player or item.Use a key unique per subscription and player.
409IDEMPOTENT_REPLAY_MISMATCH (+ mismatched_fields)Same key, same player and item, different material terms (amount_usd, interval, interval_count, wallet_only, funding_rail, trial_amount_usd).Use /amount or /payment-method to change terms, or a new key.
409REVENUE_SHARE_EXISTSShare already set.Nothing; the share is set once at create.
409PHONE_SHARE_APPROVAL_REQUIRED (+ approval_id, expires_at, next_endpoint)The phone belongs to another identity.Complete the approval flow in the body, then retry.
400DATA_CONFLICTA value the database refused.Retry with a new key after fixing the data.
400STEAMID_REQUIRED, INVALID_USERSESSION, MISSING_PLAYER_IP, STEAM_INTERVAL_COUNT_UNSUPPORTED, CARD_FIELDS_NOT_ALLOWED, TRIAL_NOT_SUPPORTED_ON_STEAM, AMOUNT_TOO_SMALL_FOR_STEAM, STEAM_REQUEST_INVALIDSteam field rules.See the Steam road.
403STEAM_RAIL_NOT_ENTITLED, RAIL_NOT_AVAILABLE_TO_PLATFORMTitle not enabled for Steam / rail not available to your tenant type.Enable Steam for the title in the console; contact Invo.
409STEAM_CHANNEL_REQUIRED, STEAM_ACCOUNT_LOCKED, STEAM_CHARGE_UNPLANNABLE, PARTNER_BILLING_NOT_SET_UP, PARTNER_RAIL_SUSPENDED, PARTNER_CREDIT_UNAVAILABLESteam gates.See the Steam road.
409STEAM_AUTHORIZATION_PENDING (+ subscription_id)A Steam subscription for this player and item is waiting for authorisation and this request carries different terms.Finalize, cancel or wait out the pending row, or send the same terms to get it back.
503STEAM_NOT_CONFIGURED, STEAM_UNAVAILABLE, {"message": "Steam subscriptions are not enabled."}Steam configuration missing / Steam down / rail off.Configure Steam; retry with a new key; contact Invo.
502STEAM_ERRORSteam could not start the subscription; row abandoned.Retry with a new key.
500CLAIM_FAILED, ORDER_FAILEDInvo could not open the first period.Retry with a new key.

Finalize (/steam/finalize)

HTTPCode or bodyMeaningRemedy
400SUBSCRIPTION_ID_REQUIREDMissing body field.Send the id.
409{"status": "not_authorized", "steam_status": "..."}The member has not completed authorisation yet.Retry after they have.
409NOT_A_STEAM_SUBSCRIPTIONNot a Steam subscription.Nothing to finalize.
409SUBSCRIPTION_TERMINALCanceled or expired.Start a new one.
409NO_STEAM_AUTHORIZATIONNo open Steam authorisation.Start a new subscription.
409STEAM_APP_CHANGEDStarted against a different Steam application.Contact Invo.
503{"status": "service_unavailable"}Steam unavailable.Retry.
502{"status": "steam_error"}Could not verify the authorisation.Retry.

Read, list, cancel, amount, payment-method

HTTPCode or bodyMeaningRemedy
400PLAYER_EMAIL_REQUIRED, PLAYER_EMAIL_TOO_LONGList without a valid email.Send the email.
400STATUS_INVALIDUnknown status in the list filter.Use the statuses on the overview or live.
400PAGINATION_INVALIDlimit outside 1 to 100 or offset outside 0 to 100000.Fix the parameter.
409SUBSCRIPTION_TERMINALAmount or payment-method on a canceled or expired row.Start a new subscription.
409STEAM_REAUTHORIZATION_REQUIRED (+ current_amount_usd, requested_amount_usd)Price increase on Steam.Start a new Steam subscription at the new price; cancel this one.
400PAYMENT_METHOD_REQUIREDNo card id in the body.Send the id.
400WALLET_ONLY_CONFLICTwallet_only: true together with a card.Send one or the other.
409WALLET_ONLY_SUBSCRIPTIONCard attached to a wallet-only subscription without wallet_only: false.Pass wallet_only: false.
409NOT_A_CARD_SUBSCRIPTIONPayment-method on Steam.Not available on Steam.
409PAYMENT_METHOD_UNAVAILABLEThe card was removed concurrently.Pick another card.
404PAYMENT_METHOD_NOT_FOUNDNot this player’s card in this title.Use a card from /player-cards.
400PAYMENT_METHOD_EXPIRED, PAYMENT_METHOD_INVALIDExpired or malformed id.Use an unexpired card id.

Refund (/refund)

Not an error: 202 with status: "pending_approval" (the refund exceeded the daily self-serve limit and became a refund request; nothing moved), and warnings codes REASON_RECOMMENDED and REASON_TRUNCATED on a 200 or 202. See Refunds.

HTTPCode or bodyMeaningRemedy
403REFUNDS_NOT_ENABLEDRefunds are turned off for this game (they are on by default).Contact Invo.
400CLIENT_REQUEST_ID_REQUIRED, CLIENT_REQUEST_ID_INVALID, CLIENT_REQUEST_ID_TOO_LONG, CLIENT_REQUEST_ID_RESERVEDKey invalid.One key per refund, at most 255 chars, not sub_-prefixed.
400INVALID_PERIOD_SEQNot an integer at or above 1.Fix.
404PERIOD_NOT_FOUNDNo such period on this subscription.Check the sequence.
400INVALID_AMOUNTNot a decimal string above zero.Fix.
400AMOUNT_EXCEEDS_REMAINING (+ remaining_refundable_usd, already_refunded_usd)More than is left to refund.Refund at most the remaining amount.
409NO_PAID_PERIODNothing collected yet.Nothing to refund.
409PERIOD_NOT_REFUNDABLE (+ period_status)That period was never collected.Pick a paid period.
409ALREADY_REFUNDED (+ refunded_amount_usd, amount_usd)Fully refunded already.Nothing to do.
409REFUND_WINDOW_CLOSED (+ period_charged_at, refund_window_days, window_closed_at)The period was charged more than 30 days ago.It can no longer be refunded here. A retry of a refund that already ran still returns its receipt.
409REFUND_REQUEST_PENDING (+ request_id)A refund request for this period is waiting for Invo review.Wait for subscription.refund_approved or subscription.refund_rejected.
409REFUND_REQUEST_REJECTED (+ request_id, decision_reason)This key was a refund request Invo rejected.Use a new client_request_id to try again.
409CONCURRENT_REQUESTTwo refund requests for one period were filed at the same moment.Retry with the same key.
409STEAM_REFUND_NOT_SUPPORTEDThe period was charged through Steam. Refused before anything moves.Not supported yet. Wallet-covered periods of a Steam subscription are refundable.
409MINT_ORDER_MISSING, NO_PAYMENT_REFERENCEThe card charge’s record cannot be found.Contact Invo.
500TENANT_ROWS_MISSINGThe period’s settlement rows cannot be found for your title. Nothing moved.Contact Invo.
502REFUND_LOOKUP_FAILED, REFUND_MISMATCHNeeds manual attention. REFUND_MISMATCH also covers a partial card refund that found a processor refund for this request with a different amount, or a charge already fully refunded outside this flow.Contact Invo; do not retry in a loop.
502PROCESSOR_ERRORThe card processor refused or failed.Retry with the same key; contact Invo if it persists.
503{"error": "service_unavailable"}Temporary.Retry shortly.
503error_code_public: PROCESSOR_NOT_CONFIGUREDCard processor not configured for this environment.Contact Invo.

Hosted card page (POST /api/checkout/card-setup-sessions)

HTTPCode or bodyMeaningRemedy
400INVALID_PLAYER_EMAILplayer_email missing or not an email.Send the member’s email.
400INVALID_INPUTsuccess_url or cancel_url uses a script scheme; or, for a new member, player_name is not text, over 255 characters or has no visible characters, or player_phone is not text or over 30 characters.Use https or an app deep link; send a real name and phone.
404PLAYER_NOT_FOUNDThe member does not exist in this title and no player_name was sent. Nothing was created.Send player_name to create the member with their card (card road).
409PHONE_SHARE_APPROVAL_REQUIREDCreating the new member needs the approval of whoever already holds player_phone. Same body as on /subscribe. Nothing was created.Handle it as on /subscribe, then call again.
503PLAYER_CREATE_FAILEDThe new member could not be created. Nothing was created and no link was minted.Call again.
401{"message": "..."}Key missing, unknown, disabled or out of grace.Send the current key for this environment.
403{"message": "Game '...' is not active"}Title neither live nor testing.Contact Invo.
503{"status": "error", "error_code": "<configuration code>", "message": "Card setup is not available right now."}Card setup is not available in this environment; the code names an Invo-side configuration condition.Retry later; contact Invo if it persists. Do not branch on the code.
429{"error": "rate_limit_exceeded", "message": "...", "retry_after": n, "limit_type": "rate_limit"}Over 2000 mints per minute on one key, or 120 per minute from one IP. Retry-After header set.Honour Retry-After.

The 404 body also carries "status": "error". What the member can see on the page itself (never returned to your server; listed so support can recognise them): “This card link has expired. Ask for a new one.” (the 10-minute session lapsed, or the link is malformed; mint another), “Your card has not been authorised yet. Please try again.” (the issuer step did not finish; the same link keeps working), “We could not save your card. Please try again in a moment.” (a save failure after authorisation; the same link keeps working), “This link can no longer be used. Ask for a new card link.” (a save failure after which the link could not be handed back; mint a new one), “Only a card can be saved for a subscription. Ask for a new link and enter a card.” (terminal; mint a new link), and “This card link has already been used.” (the link was spent and no card is recorded under it; mint a new link). After a successful save, reopening the link within its 10 minutes shows the “Finish saving card” step; a click returns the same card, saves nothing twice, and redirects the member to your success_url again.

Card capture, own form (/setup-intent, /setup-intent/confirm, purchase save_card)

HTTPCode or bodyMeaningRemedy
400MISSING_SETUP_REFERENCE, INVALID_SETUP_REFERENCEReference missing or outside the character rules.Send 1 to 200 chars of A-Z a-z 0-9 . _ : -
404PLAYER_NOT_FOUNDThe player does not exist in this title.Create the player first.
409SETUP_REFERENCE_REUSEDSame reference, different parameters.Use a new reference.
400INVALID_PAYMENT_METHOD, CARD_DECLINED, SETUP_FAILEDThe card could not be saved.Ask for another card.
400RAW_CARD_NOT_SUPPORTEDRaw card numbers were sent.Send a tokenised card.
400{"status": "still_requires_action"}The client has not finished confirming.Confirm with the card element, then call confirm again.
500CARD_PERSIST_FAILEDAuthorised but not recorded.Retry /setup-intent/confirm with the same setup_intent_id.
500SETUP_CONFIRMATION_FAILEDConfirm failed unexpectedly.Retry confirm with the same id.
400INVALID_SAVE_CARDsave_card was not a JSON boolean (purchase path).Send true, not "true".

Sandbox clock tools

HTTPCode or bodyMeaningRemedy
401SANDBOX_CLOCK_UNAUTHORIZEDMissing, wrong or never-issued clock key.Reveal the key in the sandbox console (Game Settings) and send X-Sandbox-Clock-Key.
400INVALID_INTERVALS, INVALID_OUTCOMEBody value out of range.intervals 0 to 60; outcome card_declined, insufficient_funds or error.
409SUBSCRIPTION_NOT_LIVETerminal or pending row.Use a live subscription (finalize a pending Steam row first).
409ATTEMPT_IN_FLIGHTAn unresolved attempt.Wait about 30 minutes.
409AUTH_CHALLENGE_OPENA challenge is open.Complete it or let it expire.
409AMOUNT_OVER_CEILINGPrice above the 25000.00 USD per-charge ceiling.Lower the price. The ceiling is per charge, so an annual plan is measured on the whole annual amount.
409PERIOD_TOO_FAR_AHEADForced twice without advancing.Call advance-clock.
409NO_CHARGEABLE_PERIODNothing due.Call advance-clock.
409WINDOW_ALREADY_PAIDThe window is behind the paid boundary.Create a fresh subscription.
500CLAIM_FAILEDCould not claim the row.Retry.
503FLOW_PAUSEDBilling paused.Retry shortly.
404{"error": "Resource not found", "error_id": "ERR_...", "timestamp": "..."}You are outside sandbox, or the path has /api in it. The same body as any unknown path.Use https://sandbox.invo.network/sandbox/subscriptions/<id>/...

Reporting (/api/subscriptions/reporting/...)

HTTPCode or bodyMeaningRemedy
400WINDOW_INVALIDstart or end missing, unparseable, or end not after start.Send ISO 8601 dates, end after start.
400WINDOW_TOO_LONGRange over 400 days.Narrow the range.
400WINDOW_TOO_LARGE (+ counts)More than 20,000 settled or refunded renewals in the range.Run the report in parts.
400RECIPIENT_INVALIDrecipient_player_id not an integer.Fix.
400SUBSCRIPTION_ID_INVALIDsubscription_id over 50 chars.Fix.
400PAGINATION_INVALIDlimit or offset out of range.Fix.
400STATUS_INVALIDUnknown period status in the history filter.Use pending, paid, failed, forgiven, refunded.
404SUBSCRIPTION_NOT_FOUND, PERIOD_NOT_FOUNDNot on your title.Check the id.

Webhook verification (SDK)

Thrown by verifyWebhook / verify_webhook rather than returned by the API:

CodeMeaningRemedy
WEBHOOK_SIGNATURE_MISSINGNo X-Invo-Signature header.Check the request reached you unmodified.
WEBHOOK_SECRET_MISSINGYou passed no secret.Pass the signing secret from registration.
WEBHOOK_TIMESTAMP_EXPIREDThe signed timestamp is more than 300 seconds from now.Check your clock; do not queue deliveries before verifying.
WEBHOOK_SIGNATURE_INVALIDNo v1 value matched.Verify the raw body bytes, not a re-serialised object; during a rotation pass both secrets.
WEBHOOK_MALFORMEDThe body is not a valid envelope.Check for truncation or a proxy rewriting the body.