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:
- Prepare: Call the transfer or approve endpoint to get a transaction object (function signature + params)
- Sign: Have the user sign the transaction with their passkey
- 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
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"
}'| Field | Type | Required | Description |
|---|---|---|---|
walletAddress | string | Yes | The smart wallet address to transfer from (must belong to your org) |
tokenAddress | string | Yes | The ERC-20 token contract address |
to | string | Yes | The destination address |
amount | string | Yes | Amount in the token's smallest unit (e.g. wei for 18-decimal tokens) |
Response:
{
"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
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"
}'| Field | Type | Required | Description |
|---|---|---|---|
walletAddress | string | Yes | The smart wallet address (must belong to your org) |
tokenAddress | string | Yes | The ERC-20 token contract address |
spender | string | Yes | The address being approved to spend tokens |
amount | string | Yes | Amount 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.
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"
}'| Field | Type | Required | Description |
|---|---|---|---|
walletAddress | string | Yes | The wallet that signs and sends (must belong to your org) |
tokenAddress | string | Yes | The collection contract |
from | string | Yes | Current holder. Usually the same as walletAddress; it must also be a wallet your org owns |
to | string | Yes | Who receives it |
tokenId | string | Yes | Which item in the collection |
data | string | No | Extra 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.
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.
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.
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.
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.
{
"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.
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"
}'{
"statusCode": 200,
"content": {
"address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"name": "USD Coin",
"symbol": "USDC",
"decimals": 6
}
}