Version: 1.0.0
Category: Action
Status: Stable
The Action Protocol defines how AI agents should perform actions (transactions) in Structs. All actions are submitted as transactions to the consensus network.
{
"baseUrl": "http://localhost:1317",
"transactionEndpoint": "/cosmos/tx/v1beta1/txs",
"timeout": 30000,
"confirmationTimeout": 60000,
"retryPolicy": {
"maxRetries": 3,
"retryDelay": 2000,
"retryOn": ["timeout", "5xx", "network"]
}
}
Format: Query account to get account_number and sequence
{
"request": {
"method": "GET",
"url": "http://localhost:1317/cosmos/auth/v1beta1/accounts/{address}",
"headers": {
"Accept": "application/json"
}
},
"response": {
"status": 200,
"body": {
"account": {
"@type": "/cosmos.auth.v1beta1.BaseAccount",
"address": "structs1abc...",
"pub_key": {
"@type": "/cosmos.crypto.secp256k1.PubKey",
"key": "base64_public_key"
},
"account_number": "0",
"sequence": "5"
}
}
}
}
Extract: Store account_number and sequence for transaction signing.
Format: Create transaction body with message(s) and auth_info
{
"body": {
"messages": [
{
"@type": "/structs.structs.MsgStructBuild",
"creator": "structs1abc...",
"structType": "1",
"locationType": 1,
"locationId": "1-1"
}
],
"memo": "",
"timeout_height": "0"
},
"auth_info": {
"signer_infos": [
{
"public_key": {
"@type": "/cosmos.crypto.secp256k1.PubKey",
"key": "base64_public_key"
},
"mode_info": {
"single": {
"mode": "SIGN_MODE_DIRECT"
}
},
"sequence": "5"
}
],
"fee": {
"amount": [],
"gas_limit": "200000",
"payer": "",
"granter": ""
}
}
}
Multiple Messages: Can include multiple messages in one transaction
{
"body": {
"messages": [
{
"@type": "/structs.structs.MsgStructBuild",
"creator": "structs1abc...",
"structType": "1",
"locationType": 1,
"locationId": "1-1"
},
{
"@type": "/structs.structs.MsgReactorInfuse",
"creator": "structs1abc...",
"reactorId": "3-1",
"allocation": "..."
}
],
"memo": "",
"timeout_height": "0"
},
"auth_info": {
"signer_infos": [
{
"public_key": {
"@type": "/cosmos.crypto.secp256k1.PubKey",
"key": "base64_public_key"
},
"mode_info": {
"single": {
"mode": "SIGN_MODE_DIRECT"
}
},
"sequence": "5"
}
],
"fee": {
"amount": [],
"gas_limit": "200000"
}
}
}
Format: Sign transaction bytes with player’s private key using secp256k1
{
"signing": {
"method": "secp256k1",
"privateKey": "0x...",
"message": "transaction_bytes",
"chainId": "structs-testnet",
"accountNumber": "0",
"sequence": "5",
"result": "base64_signature"
},
"signedTransaction": {
"body": {
"messages": [
{
"@type": "/structs.structs.MsgStructBuild",
"creator": "structs1abc...",
"structType": "1",
"locationType": 1,
"locationId": "1-1"
}
],
"memo": ""
},
"auth_info": {
"signer_infos": [
{
"public_key": {
"@type": "/cosmos.crypto.secp256k1.PubKey",
"key": "base64_public_key"
},
"mode_info": {
"single": {
"mode": "SIGN_MODE_DIRECT"
}
},
"sequence": "5"
}
],
"fee": {
"amount": [],
"gas_limit": "200000"
}
},
"signatures": [
"base64_signature"
]
}
}
Note: The transaction must be serialized to bytes before signing. The signature is then added to the signatures array.
Format: POST signed transaction (base64-encoded) to endpoint
{
"request": {
"method": "POST",
"url": "http://localhost:1317/cosmos/tx/v1beta1/txs",
"headers": {
"Content-Type": "application/json"
},
"body": {
"tx_bytes": "base64_encoded_signed_transaction",
"mode": "BROADCAST_MODE_SYNC"
}
},
"response": {
"status": 200,
"body": {
"tx_response": {
"code": 0,
"txhash": "ABC123...",
"height": 12345,
"gas_used": "150000",
"gas_wanted": "200000"
}
}
}
}
Note: tx_bytes must be the complete signed transaction serialized and base64-encoded.
Broadcast Modes:
BROADCAST_MODE_SYNC: Wait for CheckTx (fast, no confirmation)BROADCAST_MODE_ASYNC: Don’t wait (fastest, no confirmation)BROADCAST_MODE_BLOCK: Wait for block inclusion (slowest, confirmed)Format: Poll transaction status using txhash
{
"poll": {
"endpoint": "GET http://localhost:1317/cosmos/tx/v1beta1/txs/{txhash}",
"interval": 1000,
"maxAttempts": 60,
"successCondition": "tx_response.code == 0",
"failureCondition": "tx_response.code != 0"
},
"response": {
"status": 200,
"body": {
"tx_response": {
"code": 0,
"txhash": "ABC123...",
"height": 12345,
"gas_used": "150000",
"gas_wanted": "200000"
}
}
}
}
Sequence Error Handling: If transaction fails with sequence mismatch:
{
"error": {
"code": 4,
"raw_log": "sequence mismatch: expected 5, got 4"
},
"recovery": {
"action": "Get current account sequence",
"steps": [
"1. Query account again: GET /cosmos/auth/v1beta1/accounts/{address}",
"2. Update sequence in transaction",
"3. Re-sign transaction",
"4. Resubmit transaction"
]
}
}
Success Response (code: 0):
{
"tx_response": {
"code": 0,
"txhash": "ABC123...",
"height": 12345,
"gas_used": "150000",
"gas_wanted": "200000"
},
"handling": {
"action": "Transaction accepted",
"next": "Monitor for block inclusion if using BROADCAST_MODE_SYNC"
}
}
Error Response (code != 0):
{
"tx_response": {
"code": 1,
"codespace": "sdk",
"raw_log": "insufficient funds: insufficient account funds",
"txhash": "ABC123..."
},
"handling": {
"action": "Check tx_response.code and raw_log for error details",
"recovery": "Address insufficient funds issue before retrying"
}
}
Related Examples: See ai/examples/auth/consensus-transaction-signing.md for complete working examples.
Use Case: Single action, no dependencies
{
"action": "MsgStructBuild",
"steps": [
"1. Build transaction",
"2. Sign transaction",
"3. Submit transaction",
"4. Wait for confirmation",
"5. Handle result"
]
}
Use Case: Action requires follow-up action
{
"action": "MsgStructBuild",
"followUp": {
"action": "MsgStructBuildComplete",
"waitFor": "struct in building state",
"requires": ["proof-of-work"]
}
}
Example Flow:
MsgStructBuildMsgStructBuildCompleteUse Case: Action depends on game state
{
"action": "MsgStructAttack",
"preconditions": [
{
"check": "GET /structs/struct/{structId}",
"condition": "struct.operatingAmbit >= distance to target",
"ifFalse": "abort"
},
{
"check": "GET /structs/player/{playerId}",
"condition": "player.halted == false",
"ifFalse": "abort"
}
],
"then": "submit action"
}
Use Case: Multiple related actions
{
"actions": [
{
"@type": "/structs.structs.MsgStructBuild",
...
},
{
"@type": "/structs.structs.MsgReactorInfuse",
"structs:deprecated": "MsgReactorAllocate (deprecated - use MsgReactorInfuse)",
...
}
],
"submit": "single transaction"
}
{
"requirements": {
"playerOnline": {
"check": "GET /structs/player/{playerId}",
"condition": "player.halted == false",
"error": "PLAYER_HALTED"
},
"sufficientResources": {
"check": "GET /structs/player/{playerId}",
"condition": "player.playerInventory.resources >= required",
"error": "INSUFFICIENT_RESOURCES"
},
"sufficientCharge": {
"check": "GET /structs/struct/{structId}",
"condition": "struct.charge >= required",
"error": "INSUFFICIENT_CHARGE"
},
"validLocation": {
"check": "GET /structs/planet/{planetId}",
"condition": "planet exists and accessible",
"error": "INVALID_LOCATION"
}
}
}
Actions Requiring Proof-of-Work:
MsgStructBuildCompleteMsgPlanetRaidCompleteProof-of-Work Process:
{
"proofOfWork": {
"difficulty": "from struct type or planet",
"compute": {
"algorithm": "hash",
"input": "structId + nonce",
"target": "difficulty threshold"
},
"submit": {
"hash": "computed hash",
"nonce": "nonce that produces hash"
}
}
}
{
"errorCodes": {
"0": "SUCCESS",
"1": "GENERAL_ERROR",
"2": "INSUFFICIENT_FUNDS",
"3": "INVALID_SIGNATURE",
"4": "INSUFFICIENT_GAS",
"5": "INVALID_MESSAGE",
"6": "PLAYER_HALTED",
"7": "INSUFFICIENT_CHARGE",
"8": "INVALID_LOCATION",
"9": "INVALID_TARGET"
}
}
{
"onError": {
"INSUFFICIENT_FUNDS": {
"action": "log",
"retry": false,
"fallback": "wait for resources"
},
"PLAYER_HALTED": {
"action": "log",
"retry": false,
"fallback": "wait for player online"
},
"INSUFFICIENT_CHARGE": {
"action": "log",
"retry": false,
"fallback": "wait for charge"
},
"NETWORK_ERROR": {
"action": "retry",
"maxRetries": 3,
"backoff": "exponential"
}
}
}
{
"strategy": "precompute",
"when": "struct enters building state",
"action": "start computing proof-of-work",
"submit": "when ready"
}
{
"strategy": "batch",
"collect": "related actions",
"submit": "single transaction",
"benefit": "lower gas cost, atomic execution"
}
{
"strategy": "async",
"submit": "BROADCAST_MODE_ASYNC",
"poll": "separate thread",
"benefit": "non-blocking"
}
Last Updated: December 7, 2025