Before going live with real users, always ensure that you create clients using a Portal API Key from your Portal's Production environment. Read more here on going live with real users.
Create a new client
POST
https://api.portalhq.io/api/v3/custodians/me/clients
Registers a new client and returns a client API key, client session token, and whether they are AA enabled.
Request Body
Example Response
201: Created Success 400: Bad Request Bad Request 401: Unauthorized Unauthorized Request
Copy {
"id" : "string" ,
"clientApiKey" : "string" ,
"clientSessionToken" : "string" ,
"isAccountAbstracted" : boolean
}
Fetches a single client
GET
https://api.portalhq.io/api/v3/custodians/me/clients/[clientId]
Fetches the specified client for the authorized custodian.
Path Parameters
Example Response
200: OK Success 400: Bad Request Bad Request 401: Unauthorized Unauthorized Request
Copy {
"createdAt" : "2024-04-16T21:15:06.443Z" ,
"custodian" : {
"id" : "custodianId" ,
"name" : "Custodian Name"
} ,
"ejectedAt" : null ,
"environment" : {
"id" : "environmentId" ,
"name" : "Development"
} ,
"id" : "clientId" ,
"isAccountAbstracted" : false ,
"metadata" : {
"namespaces" : {
"eip155" : {
"address" : "0x6e818d8f9b6c53c59a2d957d36c2146e28906195" ,
"curve" : "SECP256K1"
}
}
} ,
"wallets" : [
{
"createdAt" : "2024-04-16T21:15:45.144Z" ,
"curve" : "SECP256K1" ,
"id" : "wallet1Id" ,
"backupSharePairs" : [
{
"backupMethod" : "PASSWORD" ,
"createdAt" : "2024-04-16T21:16:48.723Z" ,
"id" : "backupSharePairId1" ,
"status" : "completed"
} ,
{
"backupMethod" : "GDRIVE" ,
"createdAt" : "2024-04-16T21:17:02.074Z" ,
"id" : "backupSharePairId2" ,
"status" : "incomplete"
} ,
{
"backupMethod" : "ICLOUD" ,
"createdAt" : "2024-04-16T21:18:06.996Z" ,
"id" : "backupSharePairId3" ,
"status" : "incomplete"
}
] ,
"signingSharePairs" : [
{
"createdAt" : "2024-04-16T21:15:45.151Z" ,
"id" : "signingSharePairId1" ,
"status" : "completed"
}
] ,
"publicKey" : "stringifiedJSON"
}
]
}
Get Client's Assets by Chain
GET
https://api.portalhq.io/api/v3/custodians/me/clients/:clientId/chains/:chain/assets
This endpoint retrieves the asset balances (native and token balances) for a specified client and blockchain. It provides detailed information on the native balance and token balances held by a given address on the specified chain.
This endpoint is powered in partnership with Moralis .
Supported Chains
You can use either the full chain identifier or the shortcut for popular chains.
Ethereum Mainnet (eip155:1
or ethereum
)
Ethereum Sepolia (eip155:11155111
or sepolia
)
Solana Mainnet (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
or solana
)
Solana Devnet (solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1
or solana-devnet
)
Base Mainnet (eip155:8453
or base
)
Base Sepolia (eip155:84532
or base-sepolia
)
Polygon Mainnet (eip155:137
or polygon
)
Polygon Amoy (eip155:80002
or polygon-amoy
)
Optimism Mainnet (eip155:10
)
Binance Smart Chain (eip155:56
)
Binance Smart Chain Testnet (eip155:97
)
Arbitrum Mainnet (eip155:42161
)
Avalanche Mainnet (eip155:43114
)
Linea Mainnet (eip155:59140
)
Optimism Sepolia (eip155:11155420
)
Palm (eip155:11297108109
)
When using CAIP-2 chain formats in the URL (e.g. eip155:11155111
), ensure the URL is URI encoded to accommodate the ": " character.
Path Parameters
Example Response
200: Success 400: Bad Request Bad Request 401: Unauthorized Unauthorized Request
Copy {
"nativeBalance" : {
"balance" : "0.10991767582513721" ,
"decimals" : 18 ,
"name" : "Ether" ,
"rawBalance" : "109917675825137210" ,
"symbol" : "ETH" ,
"metadata" : {
"logo" : "https://cdn.moralis.io/eth/0x.png" ,
"thumbnail" : "https://cdn.moralis.io/eth/0x_thumb.png"
}
} ,
"tokenBalances" : [
{
"balance" : "0.0199" ,
"decimals" : 18 ,
"name" : "Wrapped Ether" ,
"rawBalance" : "19900000000000000" ,
"symbol" : "WETH" ,
"metadata" : {
"tokenAddress" : "0xfff9976782d46cc05630d1f6ebab18b2324d6b14" ,
"verifiedContract" : false ,
"totalSupply" : "48148.648311179713152621" ,
"rawTotalSupply" : "48148648311179713152621" ,
"percentageRelativeToTotalSupply" : 0.00004133033989114
}
}
] ,
"nfts" : [
{
"nftId" : "ethereum.0x123abc...def456.1234" ,
"name" : "Cosmic Kitty #1234" ,
"description" : "A rare, interstellar feline NFT from the Cosmic Kitties collection." ,
"imageUrl" : "https://example.com/images/cosmic-kitty-1234.png" ,
"chainId" : "eip155:1" ,
"contractAddress" : "0x123abc...def456" ,
"tokenId" : "1234" ,
"collection" : {
"name" : "Cosmic Kitties" ,
"description" : "A collection of 10,000 unique space-faring felines." ,
"imageUrl" : "https://example.com/images/cosmic-kitties-logo.png"
} ,
"lastSale" : {
"price" : 1500000000000000000 ,
"currency" : "ETH" ,
"date" : "2024-07-15T14:30:00Z"
} ,
"rarity" : {
"rank" : 42 ,
"score" : 0.95
} ,
"floorPrice" : {
"price" : 1200000000000000000 ,
"currency" : "ETH"
} ,
"detailedInfo" : {
"ownerCount" : 1 ,
"tokenCount" : 1 ,
"createdDate" : "2024-01-01T00:00:00Z" ,
"attributes" : [
{
"traitType" : "Fur Color" ,
"value" : "Nebula Purple" ,
"displayType" : null
} ,
{
"traitType" : "Eye Color" ,
"value" : "Starlight Blue" ,
"displayType" : null
} ,
{
"traitType" : "Accessory" ,
"value" : "Jetpack" ,
"displayType" : null
}
] ,
"owners" : [
{
"ownerAddress" : "0xabcdef...123456" ,
"quantity" : 1 ,
"firstAcquiredDate" : "2024-07-15T14:30:00Z" ,
"lastAcquiredDate" : "2024-07-15T14:30:00Z"
}
] ,
"extendedCollectionInfo" : {
"bannerImageUrl" : "https://example.com/images/cosmic-kitties-banner.png" ,
"externalUrl" : "https://cosmickitties.io" ,
"twitterUsername" : "@CosmicKitties" ,
"discordUrl" : "https://discord.gg/cosmickitties" ,
"instagramUsername" : "@cosmic.kitties" ,
"mediumUsername" : "@CosmicKittiesNFT" ,
"telegramUrl" : "https://t.me/cosmickitties" ,
"distinctOwnerCount" : 8500 ,
"distinctNftCount" : 10000 ,
"totalQuantity" : 10000
} ,
"extendedSaleInfo" : {
"fromAddress" : "0x98765...fedcba" ,
"toAddress" : "0xabcdef...123456" ,
"priceUsdCents" : 270000 ,
"transaction" : "0xfedcba...987654" ,
"marketplaceId" : "opensea" ,
"marketplaceName" : "OpenSea"
} ,
"marketplaceInfo" : [
{
"marketplaceId" : "opensea" ,
"marketplaceName" : "OpenSea" ,
"marketplaceCollectionId" : "cosmic-kitties" ,
"nftUrl" : "https://opensea.io/assets/ethereum/0x123abc...def456/1234" ,
"collectionUrl" : "https://opensea.io/collection/cosmic-kitties" ,
"verified" : true ,
"floorPrice" : {
"value" : 1200000000000000000 ,
"paymentToken" : {
"paymentTokenId" : "ethereum" ,
"name" : "Ethereum" ,
"symbol" : "ETH" ,
"address" : null ,
"decimals" : 18
} ,
"valueUsdCents" : 270000
}
}
] ,
"mediaInfo" : {
"previews" : {
"imageSmallUrl" : "https://example.com/images/cosmic-kitty-1234-small.png" ,
"imageMediumUrl" : "https://example.com/images/cosmic-kitty-1234-medium.png" ,
"imageLargeUrl" : "https://example.com/images/cosmic-kitty-1234-large.png" ,
"imageOpengraphUrl" : "https://example.com/images/cosmic-kitty-1234-og.png" ,
"blurhash" : "L9B4IwofV@of~qofM{of00WB%Mj[" ,
"predominantColor" : "#8A2BE2"
} ,
"animationUrl" : "https://example.com/animations/cosmic-kitty-1234.mp4" ,
"backgroundColor" : "#000000"
}
}
}
]
}
Build a "Send Asset" Transaction
POST
https://api.portalhq.io/api/v3/custodians/me/clients/:clientId/chains/:chain/assets/send/build-transaction
Creates an unsigned transaction for transferring assets to another address on a specific chain. You can then use this unsigned transaction to sign and submit the transaction.
Supported Chains
You can use any of the following friendly chain names for the chain
path param (or alternatively you can use the CAIP-2 chainId
):
sepolia
(eip155:11155111
)
base-sepolia
(eip155:84531
)
polygon-mumbai
(eip155:80001
)
solana
(solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
)
solana-devnet
(solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1
)
When using CAIP-2 chain formats in the URL (e.g. eip155:11155111
), ensure the URL is URI encoded to accommodate the ": " character.
Request Path Params
Request Body
Example Request
Copy curl --request POST \
--url http://localhost:3001/api/v3/custodians/me/clients/[clientId]/chains/ethereum/assets/send/build-transaction \
--header 'Authorization: Bearer [token]' \
--header 'Content-Type: application/json' \
--data '{
"to": "0xdFd8302f44727A6348F702fF7B594f127dE3A902",
"token": "USDC",
"amount": "0.01"
}
'
Example Response
The response includes the unsigned transaction details and metadata. The structure of the response differs depending on whether the chain is Solana or an EIP-155 chain (e.g., Ethereum, Polygon, Base).
200: EIP-155 Response 200: Solana Response 400: Bad Request Bad Request
Copy {
"transaction" : {
"from" : "0x21d8d1d2d9907c051670d8c9a80bd2192d273f8d" ,
"to" : "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" ,
"data": "0xa9059cbb000000000000000000000000dfd8302f44727a6348f702ff7b594f127de3a9020000000000000000000000000000000000000000000000000000000000002710"
} ,
"metadata" : {
"rawAmount" : "10000" ,
"formattedAmount" : "0.01" ,
"tokenDecimals" : 6 ,
"tokenAddress" : "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
}
}
Copy {
"transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAUIdDKlh5tOZP+4hxw5QGUB5WiLXZUpT35gX+MyY76LpXEnDFkvgeRwDb4A/njVdx9pXqNvR7FsgmX68XfuHQLkYQjDL4wY8hJ13iBEAciC0uIGJEPr+VGVf2qQqxBytKhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB1aziYgQ4rYMUc0A5K2y4oefHN83MNdiTvNPqWosjPIyXJY9OJInxuz0QKRSODYMLWhOZ2v8QhASOe9jb6fhZxvp6877brTo9ZfNqq8l0MbG75MLS9uDkfKYCA0UvXWEG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqU7bjXonfSSr1ztp277I64YFbNX6zvfltX7/vmtSupKIAgUGAAIEBgMHAAcDAQIACQMQJwAAAAAAAA==",
"metadata" : {
"amount" : "0.01" ,
"mintAddress" : "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" ,
"lastValidBlockHeight" : "259030944" ,
"unsignedTransaction" : {
"signatures" : null ,
"message" : {
"accountKeys" : [
"8pb5w7NTiYnwdeGu4RBB6SXrCNTL1D8nkBjEFNST4KDz" ,
"3dRpdTmiBkgLcWa7ogi8aQVAipcVrNCNh2HxPtFNZ4U8" ,
"bCtKaS7SecyaiTDL388bnLyMmgFvGdxmLmeEhcoq5Eo" ,
"11111111111111111111111111111111" ,
"8APEEA4SHrfGteABQcUmH2yEHy7nEe8DqgvmKGAStHR" ,
"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" ,
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" ,
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
] ,
"header" : {
"numRequiredSignatures" : 1 ,
"numReadonlySignedAccounts" : 0 ,
"numReadonlyUnsignedAccounts" : 5
} ,
"instructions" : [
{
"programIdIndex" : 5 ,
"accounts" : [
0 ,
2 ,
4 ,
6 ,
3 ,
7
] ,
"data" : ""
} ,
{
"programIdIndex" : 7 ,
"accounts" : [
1 ,
2 ,
0
] ,
"data" : "3GAG5eogvTjV"
}
] ,
"recentBlockhash" : "6JpyVC9BZDFVbgFqU8H2nkqvUY7vt6fmHYqSYPAbwbxT"
}
}
}
}
Copy {
"error" : "Error message"
}
Notes
The token
field can accept either a token symbol (e.g. "USDC" or "USDT") or a contract address (for EIP-155 tokens) or a mint address (for Solana tokens).
The amount
should be provided in the token's primary denomination (e.g. whole USDC, not micro-USDC).
This endpoint creates an unsigned transaction. The client is responsible for signing the transaction before broadcasting it to the network.
The response structure differs between Solana and EIP-155 chains. Make sure to handle both response types in your implementation.
Format Raw Wallet Alerts
POST
https://api.portalhq.io/api/v3/custodians/me/alerts/format
This endpoint formats a raw webhook request body for wallet events, which may include one or many transactions at once.
This endpoint is powered in partnership with Moralis .
Supported Chains
You can use either the full chain identifier or the shortcut for popular chains.
Ethereum Mainnet (eip155:1
or ethereum
)
Ethereum Sepolia (eip155:11155111
or sepolia
)
Solana Mainnet (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
or solana
)
Solana Devnet (solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1
or solana-devnet
)
Base Mainnet (eip155:8453
or base
)
Base Sepolia (eip155:84531
or base-sepolia
)
Polygon Mainnet (eip155:137
or polygon
)
Polygon Mumbai (eip155:80001
or polygon-mumbai
)
Request Body
Example Response
200: Success 400: Bad Request Bad Request 401: Unauthorized Unauthorized Request
Copy [
{
chainName : 'sepolia' ,
chainId : 'eip 155 : 11155111 ' ,
from : ' 0 xf 428e1 f 82 f 18 de 15 ef 2861 a 4 bdde 42e755998 ece' ,
to : ' 0 xdfd 8302 f 44727 a 6348 f 702 ff 7 b 594 f 127 de 3 a 902 ' ,
amount : ' 0.001 ' ,
tokenSymbol : 'USDC' ,
assetType : 'NON_NATIVE_TOKEN' ,
direction : 'OUTBOUND' ,
metadata : {
confirmed : true ,
rawAmount : ' 1000 ' ,
tokenDecimals : ' 6 ' ,
tokenAddress : ' 0 x 1 c 7 d 4 b 196 cb 0 c 7 b 01 d 743 fbc 6116 a 902379 c 7238 ' ,
tokenName : 'USDC' ,
sentAt : ' 2024-09-06 T 20 : 30 : 24.000 Z' ,
transactionHash : ' 0 x 229 c 1 cc 8 ed 898 fae 917e0 ed 90 af 94 c 624015e53 a 790 eda 1 a 7 d 30 b 1 ca 2 ffd 916 c' ,
blockNumber : ' 6645168 ' ,
fee : {
amount : ' 0.000172838817388923 ' ,
decimals : 18 ,
rawAmount : ' 172838817388923 ' ,
tokenSymbol : 'ETH'
} ,
nftTokenId : null ,
triggeredBy : ' 0 xf 428e1 f 82 f 18 de 15 ef 2861 a 4 bdde 42e755998 ece' ,
userOperationHash : ' 0 x 82 b 24 bc 1 feb 022 bcf 621 d 22 efd 473 acf 7 bdb 6 fdd 108 c 7 bbaaf 988 a 1717 edfdb 4 '
}
}
]
Resend Wallet Alert by Block Number
POST
https://api.portalhq.io/api/v3/custodians/me/alerts/retry-by-block-number
This endpoint attempts to resend a wallet event scoped by a block number in the scenario you need to replay a transaction.
This endpoint is powered in partnership with Moralis .
Supported Chains
EVM chains.
Request Body
Example Response
204: Success No Content 400: Bad Request Bad Request 401: Unauthorized Unauthorized Request
Fetches a list of clients
GET
https://api.portalhq.io/api/v3/custodians/me/clients
Fetches all clients for the authorized custodian.
Query Parameters
Example Response
200: OK Success 400: Bad Request Bad Request 401: Unauthorized Unauthorized Request
Copy {
"results" : [
{
"createdAt" : "2024-04-16T21:15:06.443Z" ,
"custodian" : {
"id" : "custodianId" ,
"name" : "Custodian Name"
} ,
"ejectedAt" : null ,
"environment" : {
"id" : "environmentId" ,
"name" : "Development"
} ,
"id" : "clientId" ,
"isAccountAbstracted" : false ,
"metadata" : {
"namespaces" : {
"eip155" : {
"address" : "0x6e818d8f9b6c53c59a2d957d36c2146e28906195" ,
"curve" : "SECP256K1"
}
}
} ,
"wallets" : [
{
"createdAt" : "2024-04-16T21:15:45.144Z" ,
"curve" : "SECP256K1" ,
"id" : "wallet1Id" ,
"backupSharePairs" : [
{
"backupMethod" : "PASSWORD" ,
"createdAt" : "2024-04-16T21:16:48.723Z" ,
"id" : "backupSharePairId1" ,
"status" : "completed"
} ,
{
"backupMethod" : "GDRIVE" ,
"createdAt" : "2024-04-16T21:17:02.074Z" ,
"id" : "backupSharePairId2" ,
"status" : "incomplete"
} ,
{
"backupMethod" : "ICLOUD" ,
"createdAt" : "2024-04-16T21:18:06.996Z" ,
"id" : "backupSharePairId3" ,
"status" : "incomplete"
}
] ,
"signingSharePairs" : [
{
"createdAt" : "2024-04-16T21:15:45.151Z" ,
"id" : "signingSharePairId1" ,
"status" : "completed"
}
] ,
"publicKey" : "stringifiedJSON"
}
]
} ,
...
] ,
"metadata" : {
"cursor" : "nextCursorToUse" ,
"take" : 100 ,
"total" : 1000
}
}
Refresh Client Session Token (CST)
POST
https://api.portalhq.io/api/v3/custodians/me/clients/[clientId]/sessions
Refreshes a Client Session Token for a client.
Path Parameters
Example Response
200: OK Success 400: Bad Request Bad Request 401: Unauthorized Unauthorized Request
Copy {
"id" : "string" ,
"clientSessionToken" : "string" ,
"isAccountAbstracted" : boolean
}
Copy {
"error" : "string"
}
Copy {
"error" : "string"
}
Generate Client Web One-Time Password (OTP)
POST
https://api.portalhq.io/api/v1/custodians/clients/[clientId]/web-otp
Generate a one-time password for a client.
Path Parameters
Example Response
200: OK Success 400: Bad Request Bad Request 401: Unauthorized Unauthorized
Copy {
"id" : "string"
"otp" : "string"
}
Format Raw Wallet Alert (@deprecated, instead use
this )
POST
https://api.portalhq.io/api/v3/custodians/me/alerts/wallets/format
This endpoint formats a raw webhook request body for wallet events.
This endpoint is powered in partnership with Moralis .
Supported Chains
You can use either the full chain identifier or the shortcut for popular chains.
Ethereum Mainnet (eip155:1
or ethereum
)
Ethereum Sepolia (eip155:11155111
or sepolia
)
Solana Mainnet (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
or solana
)
Solana Devnet (solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1
or solana-devnet
)
Base Mainnet (eip155:8453
or base
)
Base Sepolia (eip155:84531
or base-sepolia
)
Polygon Mainnet (eip155:137
or polygon
)
Polygon Mumbai (eip155:80001
or polygon-mumbai
)
Request Body
Example Response
200: Success 400: Bad Request Bad Request 401: Unauthorized Unauthorized Request
Copy {
chainName : 'sepolia' ,
chainId : 'eip 155 : 11155111 ' ,
from : ' 0 xf 428e1 f 82 f 18 de 15 ef 2861 a 4 bdde 42e755998 ece' ,
to : ' 0 xdfd 8302 f 44727 a 6348 f 702 ff 7 b 594 f 127 de 3 a 902 ' ,
amount : ' 0.001 ' ,
tokenSymbol : 'USDC' ,
assetType : 'NON_NATIVE_TOKEN' ,
direction : 'OUTBOUND' ,
metadata : {
confirmed : true ,
rawAmount : ' 1000 ' ,
tokenDecimals : ' 6 ' ,
tokenAddress : ' 0 x 1 c 7 d 4 b 196 cb 0 c 7 b 01 d 743 fbc 6116 a 902379 c 7238 ' ,
tokenName : 'USDC' ,
sentAt : ' 2024-09-06 T 20 : 30 : 24.000 Z' ,
transactionHash : ' 0 x 229 c 1 cc 8 ed 898 fae 917e0 ed 90 af 94 c 624015e53 a 790 eda 1 a 7 d 30 b 1 ca 2 ffd 916 c' ,
blockNumber : ' 6645168 ' ,
fee : {
amount : ' 0.000172838817388923 ' ,
decimals : 18 ,
rawAmount : ' 172838817388923 ' ,
tokenSymbol : 'ETH'
} ,
nftTokenId : null ,
triggeredBy : ' 0 xf 428e1 f 82 f 18 de 15 ef 2861 a 4 bdde 42e755998 ece' ,
userOperationHash : ' 0 x 82 b 24 bc 1 feb 022 bcf 621 d 22 efd 473 acf 7 bdb 6 fdd 108 c 7 bbaaf 988 a 1717 edfdb 4 '
}
}