App-controlled, embedded wallets for a smooth UX

Overview

You'll find API endpoints and key usage notes below. If you ever need help you can reach out to us.

Use Cases

Shinami’s Invisible Wallets abstract away Web3 elements like seed phrases, third-party wallet connections, gas fees, and signing popups. They are embedded, backend wallets under the shared custody of your app and Shinami. Both parties must cooperate in order to obtain a valid signature.

Shinami Gas Station Integration

All methods below that write to the Aptos blockchain have their gas fees sponsored by you via a Gas Station fund you create. This is because Invisible Wallets are designed to easily onboard Web2-native users (who may not want to download a wallet app, manage a seed phrase, and complete KYC checks to buy APT for gas). See the Aptos Gas Station FAQ page in our Help Center for how guidance on how to set up a fund.

Authentication, Rate Limits, and Error Handling

Authentication

You authenticate via an access key passed in a header ('X-Api-Key: ACCESS_KEY') or in the request url (https://api.shinami.com/aptos/wallet/v1/ACCESS_KEY). We recommend using a request header and not putting access keys in your request URLs for reduced visibility (in logs, etc). These steps are done automatically by our TypeScript SDK.

See our Authentication and API Keys guide for how to set up a Wallet-Services-only access key and an access key with Wallet Services and Gas Station rights (needed to execute transactions and initialize Invisible Wallets on chain).

🚧

Call this API from your backend

Shinami Wallet Services do not support CORS requests, so if you make requests to these APIs from your frontend you'll get a CORS error. This is for security reasons: exposed keys and wallet information could lead to malicious actors signing transactions on behalf of your users.

Rate Limits

When you surpass the QPS limit for a key, we return a JSON-RPC error code -32010. We recommend implementing retries with a backoff to handle any rate limits errors that arise. You can also adjust the rate limits of your keys to better balance your QPS allotment across your keys.

Error Handling

See our Error Reference for guidance on the errors you may receive from our services, including a section on errors specific to the Invisible Wallet API.

WalletId and Secret Pairing

When you create an Invisible Wallet, you must create, store, link, and never change the following two values:

  • walletId: Your internal id for a wallet. When you provide us a walletId in a method call, it tells us which Invisible Wallet to use. It could be your internal userId value, or a new arbitrary and unique value you link to the userId.
  • secret: Your internal secret for a wallet. The sessionToken you generate with it is combined with Shinami data to obtain a signature from the associated wallet. Ideally it would be different for each wallet so that if one secret is compromised the rest are not.

When you create an Invisible Wallet, you forever link its walletId it to the secret you used:

So, if you try to use the walletId with a different secret, you'll get an error:

Tutorial with E2E sample code

Check out our TypeScript tutorial for more code samples and details on the end-to-end flow of creating and using Invisible Wallets to execute sponsored transactions.

Methods

shinami_key_createSession

Description
For security purposes, you must generate a session token before you create a wallet, or sign/execute transactions. Session tokens are valid and can be reused for 10 minutes.

You may also use an instance of ShinamiWalletSigner to manage session token generation and refreshes for a given wallet. This is shown in the methods below that have a sessionToken parameter in an additional sample code tab.

Request Parameters

NameTypeDescription
secretstringUsed to encrypt and decrypt a wallet's private key. Therefore, it must always be used with the same walletId and cannot be changed in the future (see walletId and secret pairing)

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

Replace all instances of {{name}} with the actual value for that name

curl https://api.shinami.com/aptos/key/v1 \
-X POST \
-H 'X-API-Key: {{walletServicesAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc":"2.0", 
        "method":"key_createSession", 
        "params":[
            "{{secret}}"
        ], 
        "id":1
    }'
import { KeyClient } from "@shinami/clients/aptos";

const keyClient = new KeyClient({{walletServicesAccessKey}});

await keyClient.createSession({{secret}});

Example Response

{
     "jsonrpc":"2.0",
     "result":"eyJraWQiOiJrZXkyMDIzMDgxMSIsImVuYyI6IkEyNTZHQ00iLCJ0YWciOiI4SVpQWXlHeDlmOTd6U2NIdmN6N3lnIiwiYWxnIjoiQTI1NkdDTUtXIiwiaXYiOiJQWVJXZFJrbnNMMlNnVzhfIn0.ygDCI-NcvUcH7wYc0Bp0-59qeIfGOqLyXZGsLF4pW0M.aOAW0AwBvAWpaS-S.QmesdNIdNIYbT59RET-lNuzNMUvS-xb2exhXrAIlspnIkV3nuBx7PKC_GgJ7C1EqJx3tDtQaLLDGdrO8_s-75oK88ls5mzDRR-w2A0VdCcTH0_JwsQgijIbCKFWS0g.dULMzxZ4gGbm2unqOnzv8w",
     "id": 1
}
"eyJraWQiOiJrZXkyMDIzMDgxMSIsImVuYyI6IkEyNTZHQ00iLCJ0YWciOiI4SVpQWXlHeDlmOTd6U2NIdmN6N3lnIiwiYWxnIjoiQTI1NkdDTUtXIiwiaXYiOiJQWVJXZFJrbnNMMlNnVzhfIn0.ygDCI-NcvUcH7wYc0Bp0-59qeIfGOqLyXZGsLF4pW0M.aOAW0AwBvAWpaS-S.QmesdNIdNIYbT59RET-lNuzNMUvS-xb2exhXrAIlspnIkV3nuBx7PKC_GgJ7C1EqJx3tDtQaLLDGdrO8_s-75oK88ls5mzDRR-w2A0VdCcTH0_JwsQgijIbCKFWS0g.dULMzxZ4gGbm2unqOnzv8w"

Response Data

TypeDescription
stringsessionToken corresponding to the provided secret. Valid and can be reused for 10 minutes.

wal_createWallet

Description

Creates a new Shinami invisible wallet, but not yet initialized on the Aptos chain. On the free tier you have a limit of wallet creations per month as shown on the "Aptos Wallet Services" tab of the billing page in your dashboard (where you can also see how to upgrade if needed). If you hit this limit, you will get a JSON-RPC code -32012 and should not retry. All other wallet operations will still work for the month, like signing with wallets you've already created.

Request Parameters

NameTypeDescription
walletIdstringA unique ID you maintain for the wallet. Can be based on your internal user IDs. Note: you cannot change this value in the future, so do not use a value that you or your users might change, such as a editable username.
sessionTokenstringThe token generated by shinami_key_createSession with the unalterable secret you will permanently associate with this walletId (and, ideally, only this walletId).

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

Replace all instances of {{name}} with the actual value for that name

curl https://api.shinami.com/aptos/wallet/v1 \
-X POST \
-H 'X-API-Key: {{walletServicesAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc": "2.0",
        "method": "wal_createWallet",
        "params": [
            "{{walletId}}",
            "{{sessionToken}}"
        ],
        "id": 1
    }'
import { KeyClient, WalletClient } from "@shinami/clients/aptos";

const walletClient = new WalletClient({{walletServicesAccessKey}}";
const keyClient = new KeyClient({{walletServicesAccessKey}});

let sessionToken = await keyClient.createSession({{walletSecret}});

await walletClient.createWallet(
  {{walletID}}, 
  sessionToken
);
import { WalletClient, KeyClient, ShinamiWalletSigner } from "@shinami/clients/aptos";

const walletClient = new WalletClient({{walletServicesAccessKey}});
const keyClient = new KeyClient({{walletServicesAccessKey}});

const signer = new ShinamiWalletSigner(
    {{walletId}},
    walletClient,
    {{walletSecret}},
    keyClient
);

// Return the Aptos wallet address associated with the
//  walletId, or create an Invisible Wallet tied to that 
//  walletId if none is found and return its address.
const CREATE_WALLET_IF_NOT_FOUND = true;
const INITIALIZE_ON_CHAIN_IF_CREATING = false;
let walletAddress = await signer.getAddress(
  CREATE_WALLET_IF_NOT_FOUND, 
  INITIALIZE_ON_CHAIN_IF_CREATING
);

Example Response

{
   "id" : 1,
   "jsonrpc" : "2.0",
   "result" : {
      "accountAddress" : "0x45329e69bf823b556ab930a244ab33e0a71f4065d52b4a763e51af2f4b4d214a"
   }
}
AccountAddress: https://github.com/aptos-labs/aptos-ts-sdk/blob/28803638e61a6eac877924cb6b0903df77e5842f/src/core/accountAddress.ts#L42
AccountAddress: https://github.com/aptos-labs/aptos-ts-sdk/blob/28803638e61a6eac877924cb6b0903df77e5842f/src/core/accountAddress.ts#L42

Response Fields
An object with the following keys:

NameTypeDescription
accountAddresscURL
string

SDK
AccountAddress
The Aptos address of created wallet.

wal_initializeWalletOnChain

Description

Initializes a Shinami Invisible Wallet on the Aptos network the Gas Station rights of your access key is associated with. This comes after a call to wal_createWallet but is not always needed. That's because initialization happens automatically if you send a transaction with an uninitialized wallet (e.g. calling wal_executeGaslessTransaction). We provide this method to give you flexibility.

Access Key Requirements: In addition to Wallet Services rights, making this request requires an access key with Node Service and Gas Station rights for the Aptos network you wish to initialize the wallet on. This is because initialization involves submitting a basic transaction with the Invisible Wallet as the sender and the gas fee sponsored by your Gas Station fund. See how to make one here. See also the next section:

Gas Station fund required: You need a Gas Station fund with APT in order to sponsorship transactions for an Invisible Wallet, including the transaction that explicitly initializes it on chain. For information on how to set this up, see the Aptos Gas Station FAQ page in our Help Center.

Shinami sponsorship fees: See our Aptos Billing FAQ.

Request Parameters

NameTypeDescription
walletIdstringA unique ID you maintain for the wallet. Can be based on your internal user IDs. Note: you cannot change this value in the future, so do not use a value that you or your users might change, such as a editable username.
sessionTokenstringThe token generated by shinami_key_createSession with the unalterable secret you will permanently associate with this walletId (and, ideally, only this walletId).

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

Replace all instances of {{name}} with the actual value for that name

curl https://api.shinami.com/aptos/wallet/v1 \
-X POST \
-H 'X-API-Key: {{walletServicesAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc": "2.0",
        "method": "wal_initializeWalletOnChain",
        "params": [
            "{{walletId}}",
            "{{sessionToken}}"
        ],
        "id": 1
    }'
import { KeyClient, WalletClient } from "@shinami/clients/aptos";

const walletClient = new WalletClient({{walletServicesAccessKey}});
const keyClient = new KeyClient({{walletServicesAccessKey}});

let sessionToken = await keyClient.createSession({{walletSecret}});

await walletClient.initializeWalletOnChain(
  {{walletID}}, 
  sessionToken
);
import { WalletClient, KeyClient, ShinamiWalletSigner } from "@shinami/clients/aptos";

const walletClient = new WalletClient({{walletServicesAccessKey}});
const keyClient = new KeyClient({{walletServicesAccessKey}});

const signer = new ShinamiWalletSigner(
    {{walletId}},
    walletClient,
    {{walletSecret}},
    keyClient
);

// Return the Aptos wallet address associated with the
//  walletId, or create an Invisible Wallet tied to that 
//  walletId if none is found and return its address. 
//  Initialize it on the network the access key's Gas 
//  Station rights are associated with.
const CREATE_WALLET_IF_NOT_FOUND = true;
const INITIALIZE_ON_CHAIN_IF_CREATING = true;
let walletAddress = await signer.getAddress(
  CREATE_WALLET_IF_NOT_FOUND, 
  INITIALIZE_ON_CHAIN_IF_CREATING
);

Example Response

{
   "id" : 1,
   "jsonrpc" : "2.0",
   "result" : {
      "accountAddress" : "0x45329e69bf823b556ab930a244ab33e0a71f4065d52b4a763e51af2f4b4d214a"
   }
}
AccountAddress: https://github.com/aptos-labs/aptos-ts-sdk/blob/28803638e61a6eac877924cb6b0903df77e5842f/src/core/accountAddress.ts#L42
AccountAddress: https://github.com/aptos-labs/aptos-ts-sdk/blob/28803638e61a6eac877924cb6b0903df77e5842f/src/core/accountAddress.ts#L42

Response Fields

An object with the following keys:

NameTypeDescription
accountAddresscURL
string

SDK
AccountAddress
The Aptos address of created wallet.

wal_createWalletOnChain

Description

Creates and initializes a Shinami invisible wallet onto the Aptos chain. On the free tier you have a limit of wallet creations per month as shown on the "Aptos Wallet Services" tab of the billing page in your dashboard (where you can also see how to upgrade if needed). If you hit this limit, you will get a JSON-RPC code -32012 and should not retry. All other wallet operations will still work for the month, like signing with wallets you've already created.

Access Key Requirements: In addition to Wallet Services rights, making this request requires an access key with Node Service rights and Gas Station rights for the Aptos network you wish to initialize the wallet on. This is because initialization involves submitting a basic transaction with the Invisible Wallet as the sender and the gas fee sponsored by your Gas Station fund. See how to make one here. See also the next section:

Gas Station fund required: You need a Gas Station fund with APT in order to sponsorship transactions for an Invisible Wallet, including the transaction that explicitly initializes it on chain. For information on how to set this up, see the Aptos Gas Station FAQ page in our Help Center.

Shinami sponsorship fees: See our Aptos Billing FAQ.

Request Parameters

NameTypeDescription
walletIdstringA unique ID you maintain for the wallet. Can be based on your internal user IDs. Note: you cannot change this value in the future, so do not use a value that you or your users might change, such as a editable username.
sessionTokenstringThe token generated by shinami_key_createSession with the unalterable secret you will permanently associate with this walletId (and, ideally, only this walletId).

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

Replace all instances of {{name}} with the actual value for that name

curl https://api.shinami.com/aptos/wallet/v1 \
-X POST \
-H 'X-API-Key: {{walletServicesAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc": "2.0",
        "method": "wal_createWalletOnChain",
        "params": [
            "{{walletId}}",
            "{{sessionToken}}"
        ],
        "id": 1
    }'
import { KeyClient, WalletClient } from "@shinami/clients/aptos";

const walletClient = new WalletClient({{walletServicesAccessKey}});
const keyClient = new KeyClient({{walletServicesAccessKey}});

let sessionToken = await keyClient.createSession({{walletSecret}});

await walletClient.createWalletOnChain(
  "{{walletID}}", 
  sessionToken
);
import { WalletClient, KeyClient, ShinamiWalletSigner } from "@shinami/clients/aptos";

const walletClient = new WalletClient({{walletServicesAccessKey}});
const keyClient = new KeyClient({{walletServicesAccessKey}});

const signer = new ShinamiWalletSigner(
    {{walletId}},
    walletClient,
    {{walletSecret}},
    keyClient
);

// Return the Aptos wallet address associated with the
//  walletId, or create an Invisible Wallet tied to that 
//  walletId if none is found and return its address. 
//  Initialize it on the network the access key's Gas 
//  Station rights are associated with.
const CREATE_WALLET_IF_NOT_FOUND = true;
const INITIALIZE_ON_CHAIN_IF_CREATING = true;
let walletAddress = await signer.getAddress(
  CREATE_WALLET_IF_NOT_FOUND, 
  INITIALIZE_ON_CHAIN_IF_CREATING
);

Example Response

{
   "id" : 1,
   "jsonrpc" : "2.0",
   "result" : {
      "accountAddress" : "0x45329e69bf823b556ab930a244ab33e0a71f4065d52b4a763e51af2f4b4d214a"
   }
}
AccountAddress: https://github.com/aptos-labs/aptos-ts-sdk/blob/28803638e61a6eac877924cb6b0903df77e5842f/src/core/accountAddress.ts#L42
AccountAddress: https://github.com/aptos-labs/aptos-ts-sdk/blob/28803638e61a6eac877924cb6b0903df77e5842f/src/core/accountAddress.ts#L42

Response Fields

An object with the following keys:

NameTypeDescription
accountAddresscURL
string

SDK
AccountAddress
The Aptos address of created wallet.

wal_getWallet

Description

Gets the address of an existing wallet, regardless of whether it is initialized or uninitialized on chain.

Request Parameters

NameTypeDescription
walletIdstringA unique ID you maintain for the wallet. Can be based on your internal user IDs. Note: you cannot change this value in the future, so do not use a value that you or your users might change, such as a editable username.

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

Replace all instances of {{name}} with the actual value for that name

curl https://api.shinami.com/aptos/wallet/v1 \
-X POST \
-H 'X-API-Key: {{walletServicesAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc": "2.0",
        "method": "wal_getWallet",
        "params": [
            "{{walletId}}"
        ],
        "id": 1
    }'
import { KeyClient, WalletClient } from "@shinami/clients/aptos";

const walletClient = new WalletClient({{walletServicesAccessKey}});
const keyClient = new KeyClient({{walletServicesAccessKey}});

let sessionToken = await keyClient.createSession({{walletSecret}});

await walletClient.getWallet(
  {{walletID}}
);
import { WalletClient, KeyClient, ShinamiWalletSigner } from "@shinami/clients/aptos";

const walletClient = new WalletClient({{walletServicesAccessKey}}";
const keyClient = new KeyClient({{walletServicesAccessKey}});

const signer = new ShinamiWalletSigner(
    {{walletId}},
    walletClient,
    {{walletSecret}},
    keyClient
);

await signer.getAddress();

Example Response

{
   "id" : 1,
   "jsonrpc" : "2.0",
   "result" : {
      "accountAddress" : "0x45329e69bf823b556ab930a244ab33e0a71f4065d52b4a763e51af2f4b4d214a"
   }
}
AccountAddress: https://github.com/aptos-labs/aptos-ts-sdk/blob/28803638e61a6eac877924cb6b0903df77e5842f/src/core/accountAddress.ts#L42
AccountAddress: https://github.com/aptos-labs/aptos-ts-sdk/blob/28803638e61a6eac877924cb6b0903df77e5842f/src/core/accountAddress.ts#L42

Response Fields

An object with the following keys:

NameTypeDescription
accountAddresscURL
string

SDK
AccountAddress
The Aptos address of created wallet.

wal_signTransaction

Description

Use an initialized Shinami Invisible Wallet to sign a transaction. When the wallet is the sender and you are sponsoring as the feePayer, use wal_executeGaslessTransaction, which does all of: sign, sponsor, and submit the transaction to the Aptos network.

Notes:

  • The Invisible wallet must be initialized on chain in order to sign.
  • If you attempt to sign immediately after initializing a wallet you may get an error that the address was not found on chain, as it take a couple of seconds for the initialization to be processed by the Aptos blockchain.

Request Parameters

NameTypeDescription
walletIdstringA unique ID you maintain for the wallet. Can be based on your internal user IDs. Note: you cannot change this value in the future, so do not use a value that you or your users might change, such as a editable username.
sessionTokenstringThe token generated by shinami_key_createSession with the unalterable secret you will permanently associate with this walletId (and, ideally, only this walletId).
SDK-only
transaction
AnyRawTransactionA SimpleTransaction or a MultiAgentTransaction that does not have a fee payer. You can find an example of building a transaction in our tutorial
cURL-only
rawTransaction
Hex string | unsigned byte arrayBCS-serialized RawTransaction. Can be passed either as an unsigned byte array or a Hex string. For a Python example of how to serialize this to send to Shinami, see the Aptos Python SDK: rawTransaction tab of the Example Request Templates.
cURL-only
secondaryAddresses
string[](Optional) Required for multi-agent transactions. Array of addresses of the secondary signers for the transaction. Must be in the exact order.
cURL-only
feePayerAddress
string(Optional) Address of a feepayer for the rawTransaction.

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

Replace all instances of {{name}} with the actual value for that name

curl https://api.shinami.com/aptos/wallet/v1 \
-X POST \
-H 'X-API-Key: {{walletServicesAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc": "2.0",
        "method": "wal_signTransaction",
        "params": [
            "{{walletId}}",
            "{{sessionToken}}",
            "{{rawTransaction}}",
            "{{(optional)secondardAddresses}}",
            "{{(optional)feePayerAddress}}"
        ],
        "id": 1
    }'
import { KeyClient, WalletClient } from "@shinami/clients/aptos";

const walletClient = new WalletClient({{walletServicesAccessKey}});
const keyClient = new KeyClient({{walletServicesAccessKey}});

let sessionToken = await keyClient.createSession({{walletSecret}});

// sign a non-feePayer transaction
await walletClient.signTransaction(
  {{walletID}},
  sessionToken,
  {{transaction}}
);
import { WalletClient, KeyClient, ShinamiWalletSigner } from "@shinami/clients/aptos";

const walletClient = new WalletClient({{walletServicesAccessKey}});
const keyClient = new KeyClient({{walletServicesAccessKey}});

const signer = new ShinamiWalletSigner(
    {{walletId}},
    walletClient,
    {{walletSecret}},
    keyClient
);

// sign a non-feePayer transaction
await signer.signTransaction({{transaction}});
# Here is an example of preparing a `RawTransaction` to be sent 
#  as the `rawTransaction` parameter

from aptos_sdk.bcs import Serializer

...

  # Step 1: 
  #   Build your RawTransaction with your Invisible Wallet address as the
  #   sender address. Below, we represent your RawTransaction 
  #   with the variable `raw_transaction`
  
  # Step 2: 
  #  Prepare your RawTransaction to send to Shinami's API.
  #  Two options are shown below

  # Option 1: send as an unsigned byte array
  shinami_rawTransaction = list(raw_transaction.to_bytes())
                                                                                
  # Option 2: send as a hex string
  transaction_serializer = Serializer()
  raw_transaction.serialize(transaction_serializer)
  shinami_rawTransaction = transaction_serializer.output().hex()

Example Response

{
  "jsonrpc":"2.0",
  "result":{
    "signature":[0,32,136,128,248,41,24,171,70,49,247,26,7,233,63,120,178,130,84,171,58,188,184,203,137,23,210,92,89,62,182,10,55,2,64,239,224,55,68,196,84,93,88,60,245,168,112,48,199,204,225,105,109,15,6,64,145,58,175,225,89,230,38,71,179,223,193,212,114,240,142,212,165,123,209,71,50,202,29,114,133,212,40,240,123,38,189,76,4,208,237,156,135,159,154,143,122,206,9]
    ,
  "id":1
}
AccountAuthenticator: https://github.com/aptos-labs/aptos-core/blob/4045f57eb4b1fe2d055263d0c541f0ca76d1df42/types/src/transaction/authenticator.rs#L488-L506
AccountAuthenticator: https://github.com/aptos-labs/aptos-core/blob/4045f57eb4b1fe2d055263d0c541f0ca76d1df42/types/src/transaction/authenticator.rs#L488-L506

Response Fields

Object with the following keys:

NameTypeDescription
signaturecURL
Unsigned byte array

SDK
AccountAuthenticator
The Invisible Wallet's signature for the transaction.

wal_executeGaslessTransaction

Description

Sponsors, signs, and executes a gasless transaction from a Shinami invisible wallet. This is a convenient end-to-end method for submitting sponsored transactions to the Aptos chain as opposed to doing all the steps individually.

Access Key Requirements: In addition to Wallet Services rights, making this request requires an access key with Gas Station and Node Service rights for the Aptos network you wish to sponsor and execute the transaction on. See how to make one here. See also the next section:

Gas Station fund required: You need a Gas Station fund with APT in order to sponsorship transactions for an Invisible Wallet. For information on how to set this up, see the Aptos Gas Station FAQ page in our Help Center.

Shinami sponsorship fees: See our Aptos Billing FAQ.

Request Parameters

NameTypeDescription
walletIdstringA unique ID you maintain for the wallet. Can be based on your internal user IDs. Note: you cannot change this value in the future, so do not use a value that you or your users might change, such as a editable username.
sessionTokenstringThe token generated by shinami_key_createSession with the unalterable secret you will permanently associate with this walletId (and, ideally, only this walletId).
SDK-only
transaction
AnyRawTransactionA SimpleTransaction or a MultiAgentTransaction. You can find an example of building a transaction in our tutorial
cURL-only
rawTransaction
Hex string | unsigned byte arrayBCS-serialized RawTransaction. Can be passed either as an unsigned byte array or a Hex string. For a Python example of how to serialize this to send to Shinami, see the Aptos Python SDK: rawTransaction tab of the Example Request Templates.
secondarySignersSDK
AccountAuthenticator[]

cURL
["address" : , "signature": (Can be passed either as an unsigned byte array or a hex string.)]
(optional) Array of additional signers and their signatures. Must be in the exact order. Required for multi-agent transactions.

Example Request Template

The TypeScript example uses the Shinami Clients SDK.

Replace all instances of {{name}} with the actual value for that name

curl https://api.shinami.com/aptos/wallet/v1 \
-X POST \
-H 'X-API-Key: {{walletServicesAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc": "2.0",
        "method": "wal_executeGaslessTransaction",
        "params": [
          "{{walletId}}",
          "{{sessionToken}}",
          "{{rawTransaction}}",
          [{
             "address": "{{secondarySignerOneAddress}}",
             "signature": "{{secondarySignerOneSignature}}"
          }]
        ],
        "id": 1
    }'
curl https://api.shinami.com/aptos/wallet/v1 \
-X POST \
-H 'X-API-Key: {{walletServicesAccessKey}}' \
-H 'Content-Type: application/json' \
-d '{
        "jsonrpc":"2.0",
        "method":"wal_executeGaslessTransaction",
        "params":[
            "{{walletID}}",
            "eyJraWQiOiJrZXkyMDI0MDUwMiIsImVuYyI6IkEyNTZHQ00iLCJ0YWciOiIzY1dmODVTQnB1X0VseG5fS19yNTVnIiwiYWxnIjoiQTI1NkdDTUtXIiwiaXYiOiJMdTRxX1Y3aG9fWDdyYXZWIn0.whquEkYQM1GBK3d26VoZNuxCuI1gQMMAA5tGWvPzoGM.mUtlN_sbR9i5rhmX.Ow_jH6bifeciMSxxK49fOLZ5DY3SJ-SnvcYEjmfnJQEIdnbKOOgK7KKjln8QUNgQpIrPtOm5yVNmo8p8Pg60uA7M7a4qBwDHh3_5ffdjAbaW6IYdbfcmDy5ydWrC84nHKLpgSMWHwEltkfE.KnjXE3IAYFX67hycEoH9gQ",
            "0x006c55632dca03fe10c78f5e983cfd560ec55e587d930aa12ea50d75ea3262de0200000000000000008302a11ceb0b060000000701000602060a03101104210405252a074f42089101200000000100020103040100010004080001050304010002060607000107080501000002020202060c060c020b000108010b0001080101080102060c03010b000109000001060c010502050b000109000a6170746f735f636f696e04636f696e067369676e657204436f696e094170746f73436f696e0877697468647261770a616464726573735f6f66076465706f7369740000000000000000000000000000000000000000000000000000000000000001000001110a0006640000000000000038000c020a0106c80000000000000038000c030b0111010b0238010b0011010b033801020000400d0300000000006400000000000000b51f8c660000000002",
            [{
                "address": "0x7e3a6349ad8a43a6b7c3307b4b05d9008548a11673f440af5e516a9bc5114b3d",
                "signature": "0x0020924795a3b1508fa51bed2b8539075eb410665114422aa80aac88732e3b92adbf4064a77a74aad50c1229dd65543d9d1cdd96542484328188ecaba585de6e22aff5de3b5bb65f76a630e5ab1ff2e32e62b59d31e65206b8e224f397db5bb36bc500"
              }]
        ],
        "id":1
    }'
import { KeyClient, WalletClient } from "@shinami/clients/aptos";

const walletClient = new WalletClient({{walletServicesAccessKey}});
const keyClient = new KeyClient({{walletServicesAccessKey}});

let sessionToken = await keyClient.createSession({{walletSecret}});

await walletClient.executeGaslessTransaction(
  {{walletID}},
  sessionToken,
  {{transaction}},
  [{{secondarySignatureOne}}, ...]
);
import { WalletClient, KeyClient, ShinamiWalletSigner } from "@shinami/clients/aptos";

const walletClient = new WalletClient({{walletServicesAccessKey}});
const keyClient = new KeyClient({{walletServicesAccessKey}});

const signer = new ShinamiWalletSigner(
    {{walletId}},
    walletClient,
    {{walletSecret}},
    keyClient
);

await signer.executeGaslessTransaction(
  {{transaction}},
  [{{secondarySignatureOne}}, ...]
);
# Here is an example of preparing a `RawTransaction` to be sent 
#  as the `rawTransaction` parameter

from aptos_sdk.bcs import Serializer

...

  # Step 1: 
  #   Build your RawTransaction with your Invisible Wallet address as the
  #   sender address. Below, we represent your RawTransaction 
  #   with the variable `raw_transaction`
  
  # Step 2: 
  #  Prepare your RawTransaction to send to Shinami's API.
  #  Two options are shown below

  # Option 1: send as an unsigned byte array
  shinami_rawTransaction = list(raw_transaction.to_bytes())
                                                                                
  # Option 2: send as a hex string
  transaction_serializer = Serializer()
  raw_transaction.serialize(transaction_serializer)
  shinami_rawTransaction = transaction_serializer.output().hex()

Example Response

{
   "id" : 1,
   "jsonrpc" : "2.0",
   "result" : {
      "pendingTransaction" : {
         "expiration_timestamp_secs" : "1720213435",
         "gas_unit_price" : "150",
         "hash" : "0xfee720e44614f8164da7d58333ef3947abbe997e3ac10451ee9a56f9788d8e34",
         "max_gas_amount" : "200000",
         "payload" : {
            "arguments" : [
               "hello"
            ],
            "function" : "0xc13c3641ba3fc36e6a62f56e5a4b8a1f651dc5d9dc280bd349d5e4d0266d0817::message::set_message",
            "type" : "entry_function_payload",
            "type_arguments" : []
         },
         "sender" : "0x35d86428a7aee9863f9aa52d6aa4b583e2bcb7ee9d3e7d1cf1b6a7e13da503af",
         "sequence_number" : "4",
         "signature" : {
            "fee_payer_address" : "0x2cdcec66a48b596c923024102feda5ee759197fe844c5ba56013d3b9a99287b6",
            "fee_payer_signer" : {
               "public_key" : "0x57b9d494d31ed56f902362eef6e9d4fa8d22e390ab921bcb1678c8ec915ecbcf",
               "signature" : "0x1bb1186530bba89d8fa0d2706aba8d9908b11f7249f947e8833ecddd5dbb73f75fb8e325188631778d4ea4c8d10fafa03309f01b5373f1c5cac150b48bbddc09",
               "type" : "ed25519_signature"
            },
            "secondary_signer_addresses" : [],
            "secondary_signers" : [],
            "sender" : {
               "public_key" : "0x8880f82918ab4631f71a07e93f78b28254ab3abcb8cb8917d25c593eb60a3702",
               "signature" : "0xb67b702ab701bf88800a764639a495b11baf3d1df3553030e33f4ef92342d2962d867be5be2731cf6d3bbfe3281f99a641c9f900185c90787448d1284b2e7e0e",
               "type" : "ed25519_signature"
            },
            "type" : "fee_payer_signature"
         }
      }
   }
}
{
   "id" : 1,
   "jsonrpc" : "2.0",
   "result" : {
      "pendingTransaction" : {
         "expiration_timestamp_secs" : "1720459189",
         "gas_unit_price" : "100",
         "hash" : "0xd30736a7a854060ff449ea67451863e6134137303f7ac3439a16d46df1f9413e",
         "max_gas_amount" : "200000",
         "payload" : {
            "arguments" : [],
            "code" : {
               "abi" : {
                  "generic_type_params" : [],
                  "is_entry" : true,
                  "is_view" : false,
                  "name" : "main",
                  "params" : [
                     "&signer",
                     "&signer"
                  ],
                  "return" : [],
                  "visibility" : "public"
               },
               "bytecode" : "0xa11ceb0b060000000701000602060a03101104210405252a074f42089101200000000100020103040100010004080001050304010002060607000107080501000002020202060c060c020b000108010b0001080101080102060c03010b000109000001060c010502050b000109000a6170746f735f636f696e04636f696e067369676e657204436f696e094170746f73436f696e0877697468647261770a616464726573735f6f66076465706f7369740000000000000000000000000000000000000000000000000000000000000001000001110a0006640000000000000038000c020a0106c80000000000000038000c030b0111010b0238010b0011010b03380102"
            },
            "type" : "script_payload",
            "type_arguments" : []
         },
         "sender" : "0x6c55632dca03fe10c78f5e983cfd560ec55e587d930aa12ea50d75ea3262de",
         "sequence_number" : "2",
         "signature" : {
            "fee_payer_address" : "0x11045a6f53a6e9064fd92b45038649c334fd1afd60848a090facab14e07cf70a",
            "fee_payer_signer" : {
               "public_key" : "0x7dfd1009d1dff08cd60e900c1eccda628808016e5885bc82a66f4d97ba959594",
               "signature" : "0x6ef8afa108df6ae0c50486dc0356e9e16abb9115d99ba6fac64a5fbcdd3e85c1775e3031511679458b4a574f55dd357919278acafc63dc124b9a70a663e8c706",
               "type" : "ed25519_signature"
            },
            "secondary_signer_addresses" : [
               "0x7e3a6349ad8a43a6b7c3307b4b05d9008548a11673f440af5e516a9bc5114b3d"
            ],
            "secondary_signers" : [
               {
                  "public_key" : "0x924795a3b1508fa51bed2b8539075eb410665114422aa80aac88732e3b92adbf",
                  "signature" : "0x64a77a74aad50c1229dd65543d9d1cdd96542484328188ecaba585de6e22aff5de3b5bb65f76a630e5ab1ff2e32e62b59d31e65206b8e224f397db5bb36bc500",
                  "type" : "ed25519_signature"
               }
            ],
            "sender" : {
               "public_key" : "0xdc27624c0bf8ee11c8d6bf54e182b12a8f876eb56e66df740cb4de7adefbfa57",
               "signature" : "0xf3bd6086a1381d36ce46dae5bf6aff2d9652a7d7ba846dacbb67b858ab20a6bf230c8f5b9c6b59d27b41d00ef0f8ec0b29e7e0335d4a4dbac4e2a6469375f50f",
               "type" : "ed25519_signature"
            },
            "type" : "fee_payer_signature"
         }
      }
   }
}
{
  hash: '0x7a337a8952378b6a9174605b56c258f15b18f3a337ba766c02a624759f401c9f',
  sender: '0x35d86428a7aee9863f9aa52d6aa4b583e2bcb7ee9d3e7d1cf1b6a7e13da503af',
  sequence_number: '2',
  max_gas_amount: '200000',
  gas_unit_price: '150',
  expiration_timestamp_secs: '1720131046',
  payload: {
    function: '0xc13c3641ba3fc36e6a62f56e5a4b8a1f651dc5d9dc280bd349d5e4d0266d0817::message::set_message',
    type_arguments: [],
    arguments: [ 'hello' ],
    type: 'entry_function_payload'
  },
  signature: {
    sender: {
      public_key: '0x8880f82918ab4631f71a07e93f78b28254ab3abcb8cb8917d25c593eb60a3702',
      signature: '0x3a446c1586ccec7bb98ad2010c7a13a238b163e096343ae7e9b858b2bd6de6cd5e7beaa6dccd67154bc5014aae4296ce13eda82668c643de99cfd547560f7801',
      type: 'ed25519_signature'
    },
    secondary_signer_addresses: [],
    secondary_signers: [],
    fee_payer_address: '0x71d6c71f68f18ffab73b74b0680c790024324b882c3110d2ed81695e3102607e',
    fee_payer_signer: {
      public_key: '0x899562c7d7be4456219b6c08ebb4b0fbe5ad4525f3d74a514ff7390e9ddeb3a7',
      signature: '0x8bb1f219fa65e036953c0ecf2d6aad017a4efd76c6ea493ac42c36ca93e8bbbe256d97ddd2cd2dc7f6e8b8bd5437041eec0cdfa18e52b0c6591d03f3c0061a02',
      type: 'ed25519_signature'
    },
    type: 'fee_payer_signature'
  }
}
{
  hash: '0xd84a42c447049501d1ef334012c39b4602eb977df1c466652f59a1387c395b8f',
  sender: '0x35d86428a7aee9863f9aa52d6aa4b583e2bcb7ee9d3e7d1cf1b6a7e13da503af',
  sequence_number: '1',
  max_gas_amount: '200000',
  gas_unit_price: '150',
  expiration_timestamp_secs: '1720130820',
  payload: {
    function: '0xc13c3641ba3fc36e6a62f56e5a4b8a1f651dc5d9dc280bd349d5e4d0266d0817::message::set_message',
    type_arguments: [],
    arguments: [ 'hello' ],
    type: 'entry_function_payload'
  },
  signature: {
    sender: {
      public_key: '0x8880f82918ab4631f71a07e93f78b28254ab3abcb8cb8917d25c593eb60a3702',
      signature: '0xccc9544d93c9563195d1596fbeeec33ae041439752114504d10592821cf3f24bd0bc006671c88e83e0449b42b41b5c06ab6f16ea5e70e8d945a0887584334207',
      type: 'ed25519_signature'
    },
    secondary_signer_addresses: [],
    secondary_signers: [],
    fee_payer_address: '0x71d6c71f68f18ffab73b74b0680c790024324b882c3110d2ed81695e3102607e',
    fee_payer_signer: {
      public_key: '0x899562c7d7be4456219b6c08ebb4b0fbe5ad4525f3d74a514ff7390e9ddeb3a7',
      signature: '0x607a3b86faf10153fd2afcb1ccec258f8e35dfb985fe2725efc77859a3f0b1e9c4e79de71c8357d8f9ee6f8508db7e0042dec270355a34c3d6d2964edd5bfb0e',
      type: 'ed25519_signature'
    },
    type: 'fee_payer_signature'
  }
}

Response Fields

Object with keys:

NameTypeDescription
pendingTransactioncURL
object (same shape as a PendingTransactionResponse)

SDK
PendingTransactionResponse
The submitted transaction waiting in mempool.