# Getting started with the API 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. Console alternative Prefer a visual interface? Follow the [console getting-started guide](/products/wallet/getting-started/getting-started) instead. ## Prerequisites Sign in to the Wallet-as-a-Service (Palisade) console at `https://app.sandbox.palisade.co`. See [Creating your organisation](/products/wallet/introduction/set-up-your-organization) 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](/products/wallet/getting-started/configure-devices-and-quorums). For sandbox testing, skip this and use HSM wallets instead. ## Base URL All examples in this guide use the sandbox environment: ``` https://api.sandbox.palisade.co ``` For production, replace with `https://api.palisade.co`. ## Step 1: Create API credentials Generate API credentials in the console to authenticate your API requests. 1. Navigate to **Settings** > **API credentials**. 2. Click **Create credentials**. This opens the **Permissions set** page. 3. 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 | 1. Under **Name and description**, enter a **Name** (for example, "Sandbox API - Wallets"). The **Description** is optional. 2. 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. 3. Review the default permissions. Each permission is built from four components: - **Type** — the resource category (`balances`, `key`, or `vault`) - **Action** — the operation (`create`, `delete`, `read`, or `update`) - **Scope** — how broadly the permission applies (`org`, `vault`, or `key`) - **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. 4. Click **Generate credentials**. 5. Copy the `clientId` and `clientSecret`. Store credentials securely 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**. Scoping credentials in production 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](/products/wallet/user-interface/api/api-credentials-best-practices). ## Step 2: Authenticate Exchange your API credentials for a Bearer token. ```bash 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:** ```json { "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: ```bash export TOKEN="" export CONTROLS_TOKEN="" export TX_TOKEN="" ``` Token expiry Tokens expire after 1 hour (`expiresIn: 3600`). Request a new token before the current one expires. ## Step 3: Create a vault A vault groups related wallets together. Create one to hold your first wallet. ```bash 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:** ```json { "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. ```bash export VAULT_ID="019c8a2f-1234-7abc-9def-abcdef123456" ``` ## Step 4: Create a wallet Create an Ethereum wallet inside your vault. This example uses HSM (available in sandbox only) so you do not need a quorum. ```bash 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`: ```bash curl -X GET "https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets/$WALLET_ID" \ -H "Authorization: Bearer $TOKEN" ``` **Response (provisioned):** ```json { "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`: ```bash export WALLET_ID="019c8a3b-5678-7def-1234-567890abcdef" export WALLET_ADDRESS="0x8dCd82eC41C41627D987830128198aff4A392D89" ``` MPC wallets in production For production wallets, set `"keystore": "MPC"` and include `"quorumId": "YOUR_QUORUM_ID"`. MPC wallets require quorum device approval before moving to `PROVISIONED`. Supported blockchains Replace `ETHEREUM` with any supported blockchain. See [Supported blockchains](/products/wallet/user-interface/wallets/supported-blockchains) for the full list. ## Step 5: Fund your wallet 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: ```bash curl -X GET "https://api.sandbox.palisade.co/v2/vaults/$VAULT_ID/wallets/$WALLET_ID/balances" \ -H "Authorization: Bearer $TOKEN" ``` **Response:** ```json { "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. ## Step 6: Create a transaction policy 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: ```bash 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:** ```json { "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": [] } ``` Approval workflow 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. Policy types `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. ## Step 7: Add a destination address Register an external destination in the address book before you can send to it. This step requires a **Controls** credential. ### Create a counterparty The `details` field requires a `type` (`INDIVIDUAL`, `ORGANIZATION`, or `DAPP`) and the corresponding detail object: ```bash 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:** ```json { "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`: ```bash export COUNTERPARTY_ID="019d0623-86d1-7aef-9134-50e8d9cb3ee5" ``` Counterparty types Use `ORGANIZATION` with a `legalName` field for businesses, or `DAPP` for decentralized applications. ### Add a blockchain address ```bash 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:** ```json { "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 } ``` Approval workflow 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. ## Step 8: Enable outgoing transactions Enable outgoing transactions on the wallet. This step requires a **Wallets** credential. ```bash 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`. ## Step 9: Send a transaction Send 0.01 ETH to the approved destination address. This step requires a **Transactions** credential. ```bash 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:** ```json { "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`: ```bash export TX_ID="019c94f1-cc8c-79b4-9c0f-bc7edaabc267" ``` ## Step 10: Track the transaction Poll the transaction until it reaches a terminal status (`CONFIRMED`, `REJECTED`, or `FAILED`). Use a **Transactions** credential: ```bash 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` Use webhooks instead of polling For production integrations, set up [webhooks](/products/wallet/user-interface/integrations/manage-webhooks) to receive real-time status updates instead of polling. ## Next steps - [Manage webhooks](/products/wallet/user-interface/integrations/manage-webhooks) for real-time transaction notifications - [API credentials best practices](/products/wallet/user-interface/api/api-credentials-best-practices) for production security - [Wallet-as-a-Service (Palisade) API reference](/products/wallet/api-docs/palisade-api/palisade-api) for the complete endpoint documentation