How to prepare transfers of coins, collectibles and multi-token holdings.

Token Operations

How Token Operations Work

Token operations in the BaaS API follow a prepare-then-execute pattern:

  1. Prepare: Call the transfer or approve endpoint to get a transaction object (function signature + params)
  2. Sign: Have the user sign the transaction with their passkey
  3. Execute: Submit the signed transaction via POST /baas/transactions/execute

The prepare endpoints do not execute on-chain. They return a transaction object that you then sign and execute separately.

ERC-20 Transfer

Endpoint: POST /baas/tokens/erc20/transfer

Required scope: token:transfer

bash
curl -X POST https://gateway.dev.passkallet.com/sepolia/api/v1/passkallet/baas/tokens/erc20/transfer \
  -H "x-api-key: pk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4" \
  -H "Content-Type: application/json" \
  -d '{
    "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "tokenAddress": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
    "to": "0x9876543210fedcba9876543210fedcba98765432",
    "amount": "1000000000000000000"
  }'
FieldTypeRequiredDescription
walletAddressstringYesThe smart wallet address to transfer from (must belong to your org)
tokenAddressstringYesThe ERC-20 token contract address
tostringYesThe destination address
amountstringYesAmount in the token's smallest unit (e.g. wei for 18-decimal tokens)

Response:

json
{
  "statusCode": 200,
  "content": {
    "success": true,
    "toAddress": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
    "txObject": {
      "functionSignature": "transfer(address,uint256)",
      "params": [
        "0x9876543210fedcba9876543210fedcba98765432",
        "1000000000000000000"
      ]
    }
  }
}

ERC-20 Approve

Endpoint: POST /baas/tokens/erc20/approve

Required scope: token:approve

bash
curl -X POST https://gateway.dev.passkallet.com/sepolia/api/v1/passkallet/baas/tokens/erc20/approve \
  -H "x-api-key: pk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4" \
  -H "Content-Type: application/json" \
  -d '{
    "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "tokenAddress": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
    "spender": "0xAbCdEf1234567890AbCdEf1234567890AbCdEf12",
    "amount": "1000000000000000000"
  }'
FieldTypeRequiredDescription
walletAddressstringYesThe smart wallet address (must belong to your org)
tokenAddressstringYesThe ERC-20 token contract address
spenderstringYesThe address being approved to spend tokens
amountstringYesAmount to approve (in token's smallest unit)

Collectible (ERC-721) Transfer

Endpoint: POST /baas/tokens/erc721/safe-transfer

Required scope: token:transfer

Moves one collectible out of a wallet your organization owns. UsessafeTransferFrom, so a contract recipient that cannot receive rejects the transfer instead of swallowing the item.

bash
curl -X POST https://gateway.dev.passkallet.com/sepolia/api/v1/passkallet/baas/tokens/erc721/safe-transfer \
  -H "x-api-key: pk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4" \
  -H "Content-Type: application/json" \
  -d '{
    "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "tokenAddress": "0x1234567890abcdef1234567890abcdef12345678",
    "from": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "to": "0x9876543210fedcba9876543210fedcba98765432",
    "tokenId": "7"
  }'
FieldTypeRequiredDescription
walletAddressstringYesThe wallet that signs and sends (must belong to your org)
tokenAddressstringYesThe collection contract
fromstringYesCurrent holder. Usually the same as walletAddress; it must also be a wallet your org owns
tostringYesWho receives it
tokenIdstringYesWhich item in the collection
datastringNoExtra bytes forwarded to a receiving contract. Rarely needed

Multi-Token (ERC-1155) Transfer

Endpoint: POST /baas/tokens/erc1155/safe-transfer

Required scope: token:transfer

Moves units of a multi-token holding — a commercial licence, an edition, a ticket. Unlike a collectible these are divisible, so you say how many.

bash
curl -X POST https://gateway.dev.passkallet.com/sepolia/api/v1/passkallet/baas/tokens/erc1155/safe-transfer \
  -H "x-api-key: pk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4" \
  -H "Content-Type: application/json" \
  -d '{
    "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "tokenAddress": "0x1234567890abcdef1234567890abcdef12345678",
    "from": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "to": "0x9876543210fedcba9876543210fedcba98765432",
    "tokenId": "0",
    "amount": "500"
  }'

Multi-Token Batch Transfer

Endpoint: POST /baas/tokens/erc1155/batch-transfer

Required scope: token:transfer

Several items from one collection in a single transaction — one signature, one fee. tokenIds and amounts pair up by position, so they must be the same length; a mismatch is rejected rather than guessed at, because the chain has no way to notice that the wrong quantity moved. Up to 50 items per call.

bash
curl -X POST https://gateway.dev.passkallet.com/sepolia/api/v1/passkallet/baas/tokens/erc1155/batch-transfer \
  -H "x-api-key: pk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4" \
  -H "Content-Type: application/json" \
  -d '{
    "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "tokenAddress": "0x1234567890abcdef1234567890abcdef12345678",
    "from": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "to": "0x9876543210fedcba9876543210fedcba98765432",
    "tokenIds": ["1", "2", "3"],
    "amounts": ["10", "20", "30"]
  }'

Letting Someone Else Move Things

Two shapes, and they are not interchangeable. All three endpoints below need the token:approve scope — never token:transfer. An integration that only moves things never receives it.

One item — ERC-721 only

Endpoint: POST /baas/tokens/erc721/approve

Prefer this whenever the collection is ERC-721: it grants nothing beyond the item named, so a marketplace listing one collectible gets no rights over the rest. Pass the zero address as approved to clear it.

bash
curl -X POST https://gateway.dev.passkallet.com/sepolia/api/v1/passkallet/baas/tokens/erc721/approve   -H "x-api-key: pk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4"   -H "Content-Type: application/json"   -d '{
    "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "tokenAddress": "0x1234567890abcdef1234567890abcdef12345678",
    "approved": "0x9876543210fedcba9876543210fedcba98765432",
    "tokenId": "7"
  }'

The whole collection

Endpoints: POST /baas/tokens/erc721/set-approval-for-all and POST /baas/tokens/erc1155/set-approval-for-all

This lets one address move every item in the collection, until revoked. It is the permission that empties collections when it goes wrong — and it is also the only approval ERC-1155 defines, because the standard has nothing narrower. A marketplace handling licences or editions cannot work without it.

Send "approved": false to revoke, through the same endpoint. Granting is logged with the operator, the collection and the wallet, so an address nobody recognises can be found later without reconstructing it from calldata.

bash
curl -X POST https://gateway.dev.passkallet.com/sepolia/api/v1/passkallet/baas/tokens/erc1155/set-approval-for-all   -H "x-api-key: pk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4"   -H "Content-Type: application/json"   -d '{
    "walletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18",
    "tokenAddress": "0x1234567890abcdef1234567890abcdef12345678",
    "operator": "0x9876543210fedcba9876543210fedcba98765432",
    "approved": true
  }'

List Allowed Tokens

Endpoint: GET /baas/tokens/allowed

Required scope: domain:read

Returns the admin-managed list of default allowed tokens.

json
{
  "statusCode": 200,
  "content": [
    {
      "id": "cla1b2c3...",
      "address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
      "name": "USD Coin",
      "symbol": "USDC",
      "decimals": 6,
      "logoUrl": "https://..."
    }
  ]
}

Resolve Token On-Chain

Endpoint: POST /baas/tokens/resolve

Required scope: wallet:read

Resolves any ERC-20 token contract address to its name, symbol, and decimals by querying on-chain.

bash
curl -X POST https://gateway.dev.passkallet.com/sepolia/api/v1/passkallet/baas/tokens/resolve \
  -H "x-api-key: pk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"
  }'
json
{
  "statusCode": 200,
  "content": {
    "address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
    "name": "USD Coin",
    "symbol": "USDC",
    "decimals": 6
  }
}