Discord Placeholders
Every placeholder available in Discord webhook embeds, organised by what each event actually adds.
Placeholders insert live data into your Discord embed. Which ones are available depends on the event the webhook is subscribed to. A placeholder that doesn't exist for the event renders blank, which is the usual reason an embed looks half-empty. Check the event's section below before using a variable.
Dashboard → Integrations → Webhooks → (a webhook) → Discord Embed Template. Hovering that field shows the list for the current event.
There are eleven events but only seven distinct variable sets, because several events share identical lists. The two shared blocks are defined once below, and each event lists only what it adds.
Block A: customer (every event)
Available in all eleven events.
{store_id}
{customer.id}
{customer.steam.id}
{customer.steam.name}
{customer.steam.avatar_url}
{customer.minecraft.id}
{customer.minecraft.name}
{customer.minecraft.avatar_url}
{customer.profile.id}
{customer.profile.platform}
{customer.profile.name}
{customer.profile.avatar_url}Use {customer.profile.*} if you want one template that works everywhere. The steam and minecraft variants are blank on the wrong platform, but profile resolves to whichever identity the customer actually used, and {customer.profile.platform} tells you which.
Block B: product object
Available in Delivery, Subscription and Discord Linked to Order events. Not available in Order Completed, Refund Completed or Chargeback Created.
{product.id} {product.name}
{product.store_id} {product.description}
{product.version_id} {product.enabled}
{product.image_url} {product.label}
{product.slug} {product.price}
{product.sort_order} {product.tag_names}
{product.created_at} {product.gameserver_names}
{product.updated_at} {product.single_game_server_only}
{product.allow_one_time_purchase} {product.allow_subscription}
{product.subscription_interval_value} {product.subscription_interval_scale}
{product.remove_after_enabled} {product.remove_after_time_value}
{product.remove_after_time_scale}Delivery events
Delivery Item Added, Delivery Item Activated, Delivery Item Revoked and Delivery Item Used all share exactly the same variables.
Block A + Block B, plus:
{id} {order_id}
{product_id} {checkout_id}
{subscription_id} {execute_on_gameserver_id}
{execute_on_gameserver.id} {execute_on_gameserver.name}
{execute_on_gameserver.enabled}
{added_at} {active_at}
{expires_at} {removed_at}
{revoked_at} {revoke_reason}Because all four share one variable set, {revoke_reason} and {revoked_at} technically exist on Delivery Item Added. They'll simply be empty. Only use them on the Revoked event.
Order Completed
Block A only, no product object. Use {product_names} for a summary line.
{id} {checkout_id}
{subscription_id} {created_at}
{product_names} {product_tags}
{product_gameservers}
{currency} {tax_inclusive}
{subtotal_amount} {discount_amount}
{tax_amount} {total_amount}
{billing_email} {billing_address_country}Refund Completed
Block A, plus:
{id} {order_id}
{gateway} {currency}
{amount} {tax_amount}
{gateway_fee_amount} {platform_fee_amount}
{store_net_amount} {store_refund_amount}
{created_at}Chargeback Created
Block A, plus:
{id} {order_id}
{gateway} {currency}
{amount} {tax_amount}
{gateway_fee_amount} {platform_fee_amount}
{store_net_amount} {chargeback_at}Chargeback Created is Refund Completed minus {store_refund_amount}, with {chargeback_at} in place of {created_at}. Copying a refund template across leaves one blank field.
Subscription Activated and Subscription Renewed
Identical variable sets.
Block A + Block B, plus:
{id} {status}
{customer_id} {checkout_id}
{checkout_line_id} {billing_cycle_sequence}
{billing_email} {billing_country}
{customer_ip}
{product_id} {product_version_id}
{product_name} {product_image_url}
{currency} {interval_value}
{interval_scale}
{subtotal_amount} {tax_amount}
{discount_amount} {total_amount}
{initial_subtotal_amount} {initial_tax_amount}
{initial_discount_amount} {initial_total_amount}
{initial_giftcard_usage_amount}
{current_period_start} {current_period_end}
{created_at} {active_at}{billing_cycle_sequence} is how you tell a first payment from a renewal: 1 is the initial charge. The initial_* amounts are what they paid at signup, useful for spotting subscribers still on an old promotional price.
Subscription Canceled
Everything from Activated / Renewed, plus:
{canceled_at}
{cancel_reason}Discord Linked to Order
Block A + Block B, plus:
{order_id} {product_id}
{customer_id} {checkout_id}
{checkout_line_id} {enqueued_at}
{discord_user_id} {discord_user_name}
{discord_user_avatar_hash}
{order.id} {order.store_id}
{order.customer} {order.checkout_id}
{order.subscription_id} {order.coupon_id}
{order.currency} {order.tax_inclusive}
{order.tax_amount} {order.discount_amount}
{order.subtotal_amount} {order.total_amount}
{order.giftcard_usage_amount}
{order.created_at} {order.completed_at}
{order.billing_name} {order.billing_email}
{order.billing_address_country}
{order.customer_ip} {order.product_names}
{order.status}Think before you publish these
Several placeholders expose personal data: {billing_email}, {order.billing_name}, {customer_ip}, {order.customer_ip}, and the various country fields.
Don't put these in an embed posted to a public channel. A public sales feed should carry the player name, the product and maybe the amount, nothing that identifies the person off the server. Send anything sensitive to a private staff channel instead.
Ready-made embeds using these variables are on Steam Templates and Minecraft Templates. Full payload schemas are in the webhook developer docs.