This guide walks you through the Wallet-as-a-Service (Palisade) API from authentication to your first confirmed transaction. Every step includes a complete curl command you can run against the sandbox environment.
Prefer a visual interface? Follow the console getting-started guide instead.
Sign in to the Wallet-as-a-Service (Palisade) console at https://app.sandbox.palisade.co. See Creating your organisation if this is your first login.
If you plan to use MPC wallets, set up at least one quorum first. See Configure devices and quorums. For sandbox testing, skip this and use HSM wallets instead.
All examples in this guide use the sandbox environment:
https://api.sandbox.palisade.coFor production, replace with https://api.palisade.co.
Generate API credentials in the console to authenticate your API requests.
- Navigate to Settings > API credentials.
- Click Create credentials. This opens the Permissions set page.
- Under Permission set type, select Wallets. Each type provides a different set of permissions:
| Permission set type | What it grants |
|---|---|
| Wallets | Manage wallets and vaults |
| Transactions | Manage transactions |
| Controls | Manage policies and addresses |
| Monitoring | Webhooks and auditing |
Under Name and description, enter a Name (for example, "Sandbox API - Wallets"). The Description is optional.
Under IP addresses, select All IP addresses for sandbox testing. In production, use Limited IP addresses and enter a comma-separated list of IPs or CIDRs.
Review the default permissions. Each permission is built from four components:
- Type — the resource category (
balances,key, orvault) - Action — the operation (
create,delete,read, orupdate) - Scope — how broadly the permission applies (
org,vault, orkey) - Resource — a specific vault or wallet (optional — leave blank to apply to all resources in the scope)
The permission set type pre-populates sensible defaults. Click Add another permission to add more, or Remove to delete one. Since this is your first credential and you haven't created vaults or wallets yet, keep the scope set to org so the credential works for all resources you create later.
- Type — the resource category (
Click Generate credentials.
Copy the
clientIdandclientSecret.
The clientSecret is shown only once. Store it in a secrets manager or environment variable — never commit it to source control.
Repeat steps 2–8 to create credentials for Transactions and Controls — you need all three permission set types to complete this walkthrough. You can also create a single credential and add permissions from multiple types using Add another permission.
Once you have vaults and wallets, you can create narrower credentials scoped to specific resources. For example, a credential with vault scope and a specific vault selected under Resource only has access to that vault. See API credentials best practices.
Exchange your API credentials for a Bearer token.
curl -X POST https://api.sandbox.palisade.co/v2/credentials/oauth/token \
-H "Content-Type: application/json" \
-d '{
"clientId": "YOUR_CLIENT_ID",
"clientSecret": "YOUR_CLIENT_SECRET"
}'Response:
{
"accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"scope": "balances:read:org:id=... vault:create:org:id=... key:read:org:id=...",
"expiresIn": 3600,
"tokenType": "Bearer"
}The scope field lists each granted permission in the format type:action:scope:id=orgId:*.
Authenticate each credential and export the tokens. The examples in this guide use $TOKEN for the Wallets credential, $CONTROLS_TOKEN for Controls, and $TX_TOKEN for Transactions:
export TOKEN="<Wallets accessToken>"
export CONTROLS_TOKEN="<Controls accessToken>"
export TX_TOKEN="<Transactions accessToken>"Tokens expire after 1 hour (expiresIn: 3600). Request a new token before the current one expires.
A vault groups related wallets together. Create one to hold your first wallet.
curl -X POST https://api.sandbox.palisade.co/v2/vaults \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Sandbox Testing",
"description": "Vault for API getting-started walkthrough"
}'Response:
{
"id": "019c8a2f-1234-7abc-9def-abcdef123456",
"organizationId": "21c81319-5b83-45f9-b648-42055084af15",
"createdBy": "a55df9e0-e14a-4410-a983-12afae46662f",
"createdAt": "2025-05-08T10:00:00.000Z",
"updatedAt": "2025-05-08T10:00:00.000Z",
"name": "Sandbox Testing",
"description": "Vault for API getting-started walkthrough"
}Save the vault id — you need it for the next step.
export VAULT_ID="019c8a2f-1234-7abc-9def-abcdef123456"Create an Ethereum wallet inside your vault. This example uses HSM (available in sandbox only) so you do not need a quorum.
curl -X POST https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "ETH Sandbox Wallet",
"description": "Ethereum wallet for API testing",
"blockchain": "ETHEREUM",
"keystore": "HSM"
}'The initial response has status: "CREATED" with an empty address. HSM wallets are provisioned within seconds. Poll the wallet until status changes to PROVISIONED:
curl -X GET "https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets/$WALLET_ID" \
-H "Authorization: Bearer $TOKEN"Response (provisioned):
{
"id": "019c8a3b-5678-7def-1234-567890abcdef",
"vaultId": "019c8a2f-1234-7abc-9def-abcdef123456",
"organizationId": "...",
"name": "ETH Sandbox Wallet",
"description": "Ethereum wallet for API testing",
"address": "0x8dCd82eC41C41627D987830128198aff4A392D89",
"publicKey": "04b05c60b4ac57b85c8d69a98409c5c5b4590d77...",
"keystore": "HSM",
"blockchain": "ETHEREUM",
"settings": {
"enabled": false,
"rawSigningEnabled": false,
"sweepingEnabled": false,
"defaultFreezeEnabled": false
},
"status": "PROVISIONED",
"keyAlgorithm": "SECP256K1"
}Note that settings.enabled is false — the wallet is deposit-only until you explicitly enable outgoing transactions in a later step.
Save the wallet id and address:
export WALLET_ID="019c8a3b-5678-7def-1234-567890abcdef"
export WALLET_ADDRESS="0x8dCd82eC41C41627D987830128198aff4A392D89"For production wallets, set "keystore": "MPC" and include "quorumId": "YOUR_QUORUM_ID". MPC wallets require quorum device approval before moving to PROVISIONED.
Replace ETHEREUM with any supported blockchain. See Supported blockchains for the full list.
Your wallet starts with a zero balance. Send testnet funds to the wallet address from a faucet or another wallet before proceeding.
For Ethereum testnet faucets, search for "Sepolia faucet" and send testnet ETH to your wallet address.
Verify the balance:
curl -X GET "https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets/$WALLET_ID/balances" \
-H "Authorization: Bearer $TOKEN"Response:
{
"currencyCode": "USD",
"aggregatedFiatValue": "0",
"balances": [
{
"asset": {
"symbol": "ETH",
"name": "Ethereum",
"blockchain": "ETHEREUM",
"standard": "NATIVE",
"decimals": 18
},
"balance": "0",
"availableBalance": "0",
"pendingBalance": "0",
"frozenBalance": "0"
}
]
}Wait until balance shows a non-zero amount before continuing.
Wallets are deposit-only by default. Create a policy rule to permit outgoing transactions. This step requires a Controls credential.
This example creates a per-transaction limit of 10 ETH:
curl -X PUT "https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets/$WALLET_ID/policy-rules/limits" \
-H "Authorization: Bearer $CONTROLS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"limitQty": "10",
"limitType": "PER_TX",
"symbol": "ETH"
}'Response:
{
"id": "c2655be7-795e-47d3-b4e9-2fc38b26febf",
"organizationId": "...",
"walletId": "019c8a3b-5678-7def-1234-567890abcdef",
"vaultId": "019c8a2f-1234-7abc-9def-abcdef123456",
"limitQty": "10",
"limitType": "PER_TX",
"asset": {
"symbol": "ETH",
"name": "Ethereum",
"blockchain": "ETHEREUM"
},
"status": "LIMIT_CREATION_APPROVAL_PENDING",
"active": false,
"matchers": []
}If your organisation has approval groups configured, the policy starts with status: "LIMIT_CREATION_APPROVAL_PENDING" and active: false until an approver approves it in the console. If no approval groups are configured, the policy auto-transitions to active.
PER_TX limits a single transaction. ROLLING_DURATION limits the total over a time window (set "duration": "86400s" for 24 hours). CONSTANT sets a lifetime cap.
Register an external destination in the address book before you can send to it. This step requires a Controls credential.
The details field requires a type (INDIVIDUAL, ORGANIZATION, or DAPP) and the corresponding detail object:
curl -X POST https://api.sandbox.palisade.co/v2/counterparties \
-H "Authorization: Bearer $CONTROLS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Test Recipient",
"description": "Sandbox test counterparty",
"details": {
"type": "INDIVIDUAL",
"individual": {
"firstName": "Test",
"lastName": "Recipient"
}
}
}'Response:
{
"id": "019d0623-86d1-7aef-9134-50e8d9cb3ee5",
"organizationId": "...",
"name": "Test Recipient",
"description": "Sandbox test counterparty",
"details": {
"type": "INDIVIDUAL",
"individual": {
"firstName": "Test",
"lastName": "Recipient"
}
},
"addressCount": 0
}Save the counterparty id:
export COUNTERPARTY_ID="019d0623-86d1-7aef-9134-50e8d9cb3ee5"Use ORGANIZATION with a legalName field for businesses, or DAPP for decentralized applications.
curl -X POST "https://api.sandbox.palisade.co/v2/counterparties/$COUNTERPARTY_ID/addresses" \
-H "Authorization: Bearer $CONTROLS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"termsAndConditionsAccepted": true,
"details": {
"type": "EXTERNAL",
"externalAddress": {
"address": "0xDESTINATION_ADDRESS",
"name": "Recipient ETH Wallet",
"blockchains": ["ETHEREUM"]
}
}
}'Replace 0xDESTINATION_ADDRESS with a valid Ethereum address. The API validates the address checksum against the selected blockchain.
Response:
{
"addressId": "019d0624-b5d7-77cf-8eee-b72ceac0f71e",
"counterpartyId": "019d0623-86d1-7aef-9134-50e8d9cb3ee5",
"organizationId": "...",
"termsAndConditionsAccepted": true,
"details": {
"type": "EXTERNAL",
"externalAddress": {
"address": "0xDESTINATION_ADDRESS",
"name": "Recipient ETH Wallet",
"blockchains": ["ETHEREUM"]
}
},
"status": "CREATION_APPROVAL_PENDING",
"active": false
}If your organisation has approval groups configured, the address starts with status: "CREATION_APPROVAL_PENDING" until an approver approves it. If no approval groups are configured, the address auto-transitions to active.
Enable outgoing transactions on the wallet. This step requires a Wallets credential.
curl -X PUT "https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets/$WALLET_ID/settings" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"settings": {
"enabled": true
}
}'The response is the full wallet object with settings.enabled now set to true.
Send 0.01 ETH to the approved destination address. This step requires a Transactions credential.
curl -X POST "https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets/$WALLET_ID/transactions/transfer" \
-H "Authorization: Bearer $TX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"destinationAddress": "0xDESTINATION_ADDRESS",
"symbol": "ETH",
"qty": "0.01"
}'Replace 0xDESTINATION_ADDRESS with the address you registered in Step 7.
Response:
{
"id": "019c94f1-cc8c-79b4-9c0f-bc7edaabc267",
"walletId": "...",
"vaultId": "...",
"status": "REQUESTED",
"action": "PALISADE_TRANSFER",
"asset": {
"symbol": "ETH"
},
"blockchain": "ETHEREUM",
"destinationAddress": "0xDESTINATION_ADDRESS",
"originAddress": "0x8dCd82eC41C41627D987830128198aff4A392D89",
"qty": "0.01"
}Save the transaction id:
export TX_ID="019c94f1-cc8c-79b4-9c0f-bc7edaabc267"Poll the transaction until it reaches a terminal status (CONFIRMED, REJECTED, or FAILED). Use a Transactions credential:
curl -X GET "https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets/$WALLET_ID/transactions/$TX_ID" \
-H "Authorization: Bearer $TX_TOKEN"The status field progresses through the lifecycle until it reaches CONFIRMED.
The transaction moves through these statuses:
REQUESTED → POLICY_CHECK_PENDING → POLICY_CHECK_PASSED → APPROVAL_CHECK_PENDING → APPROVAL_CHECK_PASSED → SIGNATURE_PENDING → SIGNED → PUBLISH_PENDING → PUBLISHED → CONFIRMATION_PENDING → CONFIRMED
For production integrations, set up webhooks to receive real-time status updates instead of polling.
- Manage webhooks for real-time transaction notifications
- API credentials best practices for production security
- Wallet-as-a-Service (Palisade) API reference for the complete endpoint documentation