V3 endpoints

These endpoints require a Portal API Key (aka "Custodian API Key") as a bearer token.

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.

Headers

NameTypeDescription

Authorization*

String

Bearer <Portal API Key>

Content-Type*

String

application/json

Request Body

NameTypeDescription

isAccountAbstracted

Boolean (optional)

The client will use ERC-4337 Account Abstraction (AA).

Example Response

{
  "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

NameTypeDescription

clientId*

String

The ID of the client.

Headers

NameTypeDescription

Authorization*

String

Bearer <Portal API Key>

Content-Type*

String

application/json

Example Response

{
  "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)

  • Fantom (eip155:250)

  • Moonbeam (eip155:1284)

  • 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

NameTypeDescription

clientId*

String

The ID of the client.

chainId*

String

Must be in CAIP-2 format or chain identifier shortcut (e.g. eip155:11155111 or sepolia).

Headers

NameTypeDescription

Authorization*

String

Bearer <Portal API Key>

Content-Type*

String

application/json

Example Response

{
  "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):

    • ethereum (eip155:1)

    • sepolia (eip155:11155111)

    • base (eip155:8453)

    • base-sepolia (eip155:84531)

    • polygon (eip155:137)

    • 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.

Headers

NameTypeDescription

Authorization*

String

Bearer <Portal API Key>

Content-Type*

String

application/json

Request Path Params

NameTypeDescription

clientId*

String

The ID of the client.

chain*

String

Must be one of the chains in "Supported Chains".

Request Body

NameTypeDescription

to*

String

The recipient's address.

token*

String

The token symbol (e.g., "USDC") or contract address or mint address (for Solana).

amount*

String

The amount to transfer (as a string, in the token's primary denomination). For example, "0.01" is 1 cent of USDC.

Example Request

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).

{
  "transaction": {
    "from": "0x21d8d1d2d9907c051670d8c9a80bd2192d273f8d",
    "to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
    "data": "0xa9059cbb000000000000000000000000dfd8302f44727a6348f702ff7b594f127de3a9020000000000000000000000000000000000000000000000000000000000002710"
  },
  "metadata": {
    "rawAmount": "10000",
    "formattedAmount": "0.01",
    "tokenDecimals": 6,
    "tokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
  }
}

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)

Headers

NameTypeDescription

Authorization*

String

Bearer <Portal API Key>

Content-Type*

String

application/json

Request Body

NameTypeDescription

chain*

"eip155"

The chain the raw webhook event is from.

rawAlert*

String

The stringified raw webhook event.

Example Response

[
  {
    chainName: 'sepolia',
    chainId: 'eip155:11155111',
    from: '0xf428e1f82f18de15ef2861a4bdde42e755998ece',
    to: '0xdfd8302f44727a6348f702ff7b594f127de3a902',
    amount: '0.001',
    tokenSymbol: 'USDC',
    assetType: 'NON_NATIVE_TOKEN',
    direction: 'OUTBOUND',
    metadata: {
      confirmed: true,
      rawAmount: '1000',
      tokenDecimals: '6',
      tokenAddress: '0x1c7d4b196cb0c7b01d743fbc6116a902379c7238',
      tokenName: 'USDC',
      sentAt: '2024-09-06T20:30:24.000Z',
      transactionHash: '0x229c1cc8ed898fae917e0ed90af94c624015e53a790eda1a7d30b1ca2ffd916c',
      blockNumber: '6645168',
      fee: {
        amount: '0.000172838817388923',
        decimals: 18,
        rawAmount: '172838817388923',
        tokenSymbol: 'ETH'
      },
      nftTokenId: null,
      triggeredBy: '0xf428e1f82f18de15ef2861a4bdde42e755998ece',
      userOperationHash: '0x82b24bc1feb022bcf621d22efd473acf7bdb6fdd108c7bbaaf988a1717edfdb4'
    }
  }
]

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.

Headers

NameTypeDescription

Authorization*

String

Bearer <Portal API Key>

Content-Type*

String

application/json

Request Body

NameTypeDescription

chainReferenceId*

String

The EVM chain reference ID. (e.g. "11155111" for Sepolia)

blockNumber*

Number

The block number of the transaction(s) you want to resend.

streamId*

String

The ID of the stream that Portal configured for you. (Reach out to our team to get this.)

Example Response

No content

Fetches a list of clients

GET https://api.portalhq.io/api/v3/custodians/me/clients

Fetches all clients for the authorized custodian.

Query Parameters

NameTypeDescription

cursor

String (optional)

The ID of the last client in the list. Used for cursor-based pagination.

take

Number (optional)

The number of records to retrieve. Maximum is 100. Default is 100.

Headers

NameTypeDescription

Authorization*

String

Bearer <Portal API Key>

Content-Type*

String

application/json

Example Response

{
  "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

NameTypeDescription

clientId*

String

The ID of a client.

Headers

NameTypeDescription

Authorization*

String

Bearer <Portal API Key>

Content-Type*

String

application/json

Example Response

{
  "id": "string",
  "clientSessionToken": "string",
  "isAccountAbstracted": boolean
}

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

NameTypeDescription

clientId*

String

The ID of a client.

Headers

NameTypeDescription

Authorization*

String

Bearer <Portal API Key>

Content-Type*

String

application/json

Example Response

{
  "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)

Headers

NameTypeDescription

Authorization*

String

Bearer <Portal API Key>

Content-Type*

String

application/json

Request Body

NameTypeDescription

chain*

"eip155"

The chain the raw webhook event is from.

rawAlert*

String

The stringified raw webhook event.

Example Response

{
  chainName: 'sepolia',
  chainId: 'eip155:11155111',
  from: '0xf428e1f82f18de15ef2861a4bdde42e755998ece',
  to: '0xdfd8302f44727a6348f702ff7b594f127de3a902',
  amount: '0.001',
  tokenSymbol: 'USDC',
  assetType: 'NON_NATIVE_TOKEN',
  direction: 'OUTBOUND',
  metadata: {
    confirmed: true,
    rawAmount: '1000',
    tokenDecimals: '6',
    tokenAddress: '0x1c7d4b196cb0c7b01d743fbc6116a902379c7238',
    tokenName: 'USDC',
    sentAt: '2024-09-06T20:30:24.000Z',
    transactionHash: '0x229c1cc8ed898fae917e0ed90af94c624015e53a790eda1a7d30b1ca2ffd916c',
    blockNumber: '6645168',
    fee: {
      amount: '0.000172838817388923',
      decimals: 18,
      rawAmount: '172838817388923',
      tokenSymbol: 'ETH'
    },
    nftTokenId: null,
    triggeredBy: '0xf428e1f82f18de15ef2861a4bdde42e755998ece',
    userOperationHash: '0x82b24bc1feb022bcf621d22efd473acf7bdb6fdd108c7bbaaf988a1717edfdb4'
  }
}

Last updated