Page Get Webhook
The page.get webhook retrieves the layout of a Game Hub page: the rows of blocks to render and, optionally, the store items to fill them with. This is useful when the page content is decided by game logic instead of being assembled in the Hub Builder.
This webhook is triggered when a player opens a page connected to a page.get webhook. Such a page has no blocks of its own. The blocks stored in Aghanim are always empty, and the layout returned by your server is what the player sees.
The webhook is also requested while a purchase is validated, to confirm the item the player is buying is still on sale on the same terms. In those requests the layout is ignored and only the items matter.
Requirements
To use the page.get webhook from Aghanim, you should have the webhook server configured as follows:
- HTTPS endpoint, accepting POST webhook requests.
- Listen for events, generated and signed by Aghanim.
- Respond with 2xx status codes to signal successful processing, and 4xx or 5xx for denial or errors.
- Respond within 10 seconds. The webhook is requested while the player waits for the page.
- Handle the request synchronously. Like
player.verify,store.get, anddaily_reward.get,page.getis delivered as a single attempt and is not covered by the retry schedule.
Configuration
- Develop a function for the
page.getwebhook processing. - Make your endpoint available.
- Register your endpoint within Aghanim account → Game → Webhooks → Add webhook by choosing the
page.getevent type. - In the section settings of the Game Hub Builder, select the webhook under Advanced → Webhook so the page takes its layout from it.
Open those settings with Add section at the bottom of the sections sidebar, which creates the page and selects the webhook at once, or from the ⋯ menu of an existing section → Edit. Connecting a webhook removes the blocks built on that page, and they cannot be restored.
Alternatively, you can register your endpoint within Aghanim using the Create Webhook API method, and connect the page by setting webhook_id with the Update Page API method. The layout_source field of page responses is read-only.
The response is not cached: the webhook is requested on every page view, so the layout can differ per player and per request.
Trigger values
| Value | Description |
|---|---|
hub.page.open | When the player opens a page with a webhook layout. |
hub.purchase | When the player starts buying an item that came from this webhook. |
order.captured | When the payment of such an item is captured. |
test | When using the "Send test event" in the Dashboard. |
On hub.purchase and order.captured the request carries no page: id and slug are empty, and Aghanim reads only the items of your response, from every block at once. Return the same items on the same terms as the ones shown to the player. An item missing from the response, or offered at other conditions, makes the purchase fail.
Without a page, Aghanim cannot tell which webhook served the layout and asks the first enabled page.get webhook of the game. In a game with several of them, the purchase can be validated against a webhook that did not serve the page.
See the full events and triggers matrix for how page.get relates to other event types.
Request schema
Below is an example of an page.get webhook request:
- HTTP
- cURL
POST /your/webhook/uri HTTP/1.1
Content-Type: application/json
Host: your-webhook-endpoint.com
User-Agent: Aghanim/0.1.0
X-Aghanim-Signature: 2e45ed4dede5e09506717490655d2f78e96d4261040ef48cc623a780bda38812
X-Aghanim-Signature-Timestamp: 1725548450
{
"event_type": "page.get",
"event_data": {
"player_id": "2D2R-OP3C",
"is_anonymous": false,
"locale": "en",
"id": "pg_2eAeXgQGcaie",
"slug": "daily-offers"
},
"event_time": 1725548450,
"event_id": "whevt_eCacGbJVbvToOgzjXUgOCitkQE",
"idempotency_key": null,
"request_id": "d1593e9c-c291-4004-8846-6679c2e5810b",
"sandbox": false,
"trigger": "hub.page.open",
"transaction_id": "whtx_eCacGbJVbvT",
"context": null,
"game_id": "gm_exTAyxPsVwh"
}
curl "https://your-webhook-endpoint.com/your/webhook/uri" \
-X POST \
-H "Content-Type: application/json" \
-H "User-Agent: Aghanim/0.1.0" \
-H "X-Aghanim-Signature: 2e45ed4dede5e09506717490655d2f78e96d4261040ef48cc623a780bda38812" \
-H "X-Aghanim-Signature-Timestamp: 1725548450" \
-d '{
"event_type": "page.get",
"event_data": {
"player_id": "2D2R-OP3C",
"is_anonymous": false,
"locale": "en",
"id": "pg_2eAeXgQGcaie",
"slug": "daily-offers"
},
"event_time": 1725548450,
"event_id": "whevt_eCacGbJVbvToOgzjXUgOCitkQE",
"idempotency_key": null,
"request_id": "d1593e9c-c291-4004-8846-6679c2e5810b",
"sandbox": false,
"trigger": "hub.page.open",
"transaction_id": "whtx_eCacGbJVbvT",
"context": null,
"game_id": "gm_exTAyxPsVwh"
}'
The Event schema
| Key | Type | Description |
|---|---|---|
event_id | string | Unique Event ID generated by Aghanim. |
game_id | string | Your game ID in the Aghanim system. |
event_type | string | The type of the event, page.get in this case. |
event_time | number | The time the event was sent, in Unix epoch time. May differ across duplicate deliveries of the same event. |
event_data | EventData | Contains the event-specific data, with possible keys for inherited objects. |
idempotency_key | string|null | Ensures webhook actions are executed only once, even if retried. Can be null depending on event type. |
request_id | string|null | If the event was triggered by an API request, the request ID is included. |
sandbox | boolean | Indicates whether the event was sent from the sandbox game environment. |
trigger | string|null | The trigger that caused the event to be sent. |
transaction_id | string | The transaction ID generated by Aghanim. This ID may be the same for multiple events emitted within the same transaction. |
context | EventContext|null | Contextual information about the event. |
The EventContext schema
| Key | Type | Description |
|---|---|---|
order | OrderContext|null | Order information associated with the event if applicable. |
player | PlayerContext|null | (Optional) Player information. To add this, enable "Add player context" in the webhook settings. |
The EventData schema
| Key | Type | Description |
|---|---|---|
player_id | string | The unique Player ID chosen for player authentication. Sent unless the game uses Composite Authorization. |
player | object | Player ID components when using Composite Authorization. Sent instead of player_id. |
is_anonymous | boolean | Indicates whether the current user is unauthenticated (anonymous). |
locale | string | The current player's locale code (ISO 639-1). Find the full list of possible locales in Locales. |
id | string | The unique page ID generated by Aghanim. Empty when the request is not about a page view. |
slug | string | The slug of the page requested by the player. Empty when the request is not about a page view. |
Response schema
Return a 2xx status code with the following JSON payload.
| Key | Type | Description | Required? |
|---|---|---|---|
blocks | [[Block]] | Rows of blocks rendered top to bottom. Up to 50 rows, each holding up to 10 blocks. | Yes |
Your server fully controls the page composition: each entry of blocks is one row, and the order of the rows and of the blocks inside a row is the order the player sees. For example, [[ImageBannerBlock], [StoreBlock]] renders a banner above the store, while [[StoreBlock], [ImageBannerBlock]] renders the store first.
Three block types are supported, distinguished by the block field: StoreBlock, ImageBannerBlock and DailyRewardsBlock. A response containing any other block type is rejected, and the page is rendered without blocks.
The StoreBlock schema
| Key | Type | Description | Required? |
|---|---|---|---|
block | string | Block type discriminator. Must be StoreBlock. | Yes |
id | string | Your own identifier of the block. | No |
store_key | string | The key of the store the block renders. Required when the block carries items. | No |
items | [Item|BundleItem] | Items of the block, in the same format as the store.get response. Up to 300 items per block. | No |
Without store_key the block renders the store setup of your game as configured in Aghanim.
The ImageBannerBlock schema
| Key | Type | Description | Required? |
|---|---|---|---|
block | string | Block type discriminator. Must be ImageBannerBlock. | Yes |
image_url | string | The image of the banner. | Yes |
title | string | The title displayed over the banner. | No |
text | string | The text displayed over the banner. | No |
button_text | string | The text of the banner button. | No |
link | string | The path the banner leads to, relative to the game hub root (for example, /store). | No |
id | string | Your own identifier of the block. | No |
The request tells you the player's locale, so the image and the texts are returned already localized. The player sees exactly the values of your response.
The DailyRewardsBlock schema
| Key | Type | Description | Required? |
|---|---|---|---|
block | string | Block type discriminator. Must be DailyRewardsBlock. | Yes |
variant | string | The layout the block renders in: expanded, compact or compact_mobile. Defaults to expanded. | No |
non_compliance | boolean | Whether the compact variant uses the non-compliance button. Ignored by the other variants. | No |
id | string | Your own identifier of the block. | No |
The block renders the daily reward program of your game as configured in Aghanim. The page.get response only places it on the page and picks its look; the rewards themselves come from the program, and from the daily_reward.get webhook when the program is webhook-driven.
Store items
Passing items inside a StoreBlock lets you deliver the layout and the store content in a single response. The items go through the same pricing, bonus and purchase-limit processing as the store.get ones, and Aghanim does not request store.get for the store they fill.
Every block with items names its store through store_key, the Key of the store in Aghanim Dashboard → Stores → the store → Settings.
The store must be of type Webhook, set by the Type field next to the Key. The type is chosen when the store is created and cannot be changed afterwards. Creating a store of this type also asks for a store.get webhook, which is not called when the items arrive with page.get.
store_key must be unique across all the store blocks of a response, whether or not they carry items, so every block that carries items needs a store of its own. A repeated key is a schema error: the whole response is rejected and the page renders empty.
Up to five blocks of a page may carry items, and "items": [] counts as carrying them.
The three states of items differ:
- No
itemsfield — the block renders the store it names, or the store setup of your game when it names none. A store of type Webhook is filled by its ownstore.getcall. "items": []— the store renders empty.- A non-empty list — the store renders exactly those items.
When store_key matches no store of the game, matches an archived or deleted one, or matches a store that is not of type Webhook, the items are dropped. The block is still rendered, filled from the store setup of your game.
The same SKU sent in two blocks of a page is rendered twice, once in each block. Items are not deduplicated across blocks.
Example of a successful response
{
"blocks": [
[
{
"block": "ImageBannerBlock",
"id": "summer-banner",
"image_url": "https://example.com/summer-sale.png",
"title": "Summer sale",
"link": "/store"
}
],
[
{
"block": "StoreBlock",
"id": "daily-store",
"store_key": "daily-deals",
"items": [
{
"sku": "crystals"
},
{
"sku": "shield",
"start_at": 1630000000,
"end_at": 1635000000
},
{
"sku": "fly_bundle",
"price": 2000,
"name": "Fly Bundle",
"description": "Fly Bundle Description",
"image_url": "https://example.com/fly-bundle.png",
"nested_items": [
{
"sku": "nested_item_sku_1"
},
{
"sku": "nested_item_sku_2",
"quantity": 200
}
]
}
]
}
],
[
{
"block": "StoreBlock",
"id": "regular-store"
}
]
]
}
Error handling
The page endpoint never fails because of the webhook. If your server times out, answers with a non-2xx status, or returns a payload that does not match the response schema, the player still receives the page, but with an empty layout, and the blocks of that page view are lost. A failed delivery is never retried: the layout is only useful while the player waits for the page. Inspect the webhook logs in Aghanim Dashboard → Game → Webhooks to see the request and the response that was rejected.
Need help?
Contact our integration team at [email protected]