Build with WalletD

How to Set Up Your Own Wallet with WalletD API

Learn how to set up a digital wallet with WalletD API. Follow curl examples to create users, fund wallets, check balances, send money, and connect webhooks.

Digital wallet connected to API code and transaction panels, illustrating the WalletD API wallet setup guide.

A wallet needs more than a balance on a screen. It needs a way to bring money in, move it between users, and explain every change. Your application also needs to handle the awkward moments: a payment that is still processing, a transfer response that never arrives, or a user who has money reserved for a purchase.

WalletD gives you APIs for those operations. You build the experience around them: sign-in, checkout, a balance screen, and transaction history. This guide walks through a first integration using curl, with a sandbox wallet for Alice and a second wallet for Bob.

Before you start

Have a WalletD tenant, its API base URL, and a sandbox API key issued for that environment. A tenant is the boundary for your application’s users and wallet configuration. If you do not have access yet, request a demo. This guide assumes an existing WalletD deployment; it does not cover installing the services yourself.

You will also need curl, jq to extract IDs from JSON responses, and a configured test payment gateway to complete the funding step. The examples assume the tenant uses USD, where 5000 minor units means $50.00. Use your tenant’s currency and its minor-unit convention if it differs.

Run these commands in a trusted terminal or from your backend environment:

export WALLETD_API="https://your-walletd-api.example"
export WALLETD_API_KEY="sk_sandbox_replace_with_your_key"

Replace both placeholders with your deployment’s values. Keep the base URL free of a trailing slash. The example hostname is not a public sandbox endpoint.

Your integration should follow this path:

Your web or mobile app → Your backend → WalletD API

Keep the tenant API key on your server. Authenticate requests to your backend and resolve the wallet user from the signed-in account before calling WalletD. Do not let a client choose an arbitrary sender ID and have your server authorize it with the tenant key.

For this walkthrough, the key needs users:write, users:read, balances:read, topups:read, topups:write, and transfers:write. Registering webhook endpoints later also requires webhooks:manage. See the authentication guide for credential types and scopes.

1. Create your wallet users

Start by mapping an account in your application to a WalletD user. external_id is your application’s stable identifier; save the returned WalletD id alongside it.

ALICE_RESPONSE=$(curl --fail-with-body -sS "$WALLETD_API/v1/users" \
  -H "Authorization: Bearer $WALLETD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_id":"wallet-guide-alice","kind":"consumer","display_name":"Alice"}')
ALICE_ID=$(printf '%s' "$ALICE_RESPONSE" | jq -er '.id')

BOB_RESPONSE=$(curl --fail-with-body -sS "$WALLETD_API/v1/users" \
  -H "Authorization: Bearer $WALLETD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_id":"wallet-guide-bob","kind":"consumer","display_name":"Bob"}')
BOB_ID=$(printf '%s' "$BOB_RESPONSE" | jq -er '.id')

Each successful response contains an id, the supplied external_id, and user details. Stop if either request fails or an ID is missing. The remaining examples depend on both IDs.

An external_id is unique within the tenant. Repeating user creation with the same identifier returns 409; it does not create another wallet. When returning to this tutorial, reuse the IDs you saved from the successful run.

There is no separate wallet-creation call in this flow. WalletD exposes each user’s balances by purpose, such as cash and points.

2. Fund Alice’s wallet

First, discover the payment gateways enabled for your deployment:

curl --fail-with-body -sS "$WALLETD_API/v1/topup_methods" \
  -H "Authorization: Bearer $WALLETD_API_KEY"

The response identifies each gateway and its checkout flow. The following example uses stripe; continue with it only if Stripe is configured in test mode for your environment. Otherwise, select an available gateway and follow its returned next_action.

Create a top-up for $50.00:

TOPUP_RESPONSE=$(curl --fail-with-body -sS "$WALLETD_API/v1/topups" \
  -H "Authorization: Bearer $WALLETD_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: wallet-guide-alice-topup-001" \
  -d "$(jq -n --arg user "$ALICE_ID" \
    '{user_id:$user,amount:5000,gateway:"stripe"}')")
TOPUP_ID=$(printf '%s' "$TOPUP_RESPONSE" | jq -er '.id')
printf '%s' "$TOPUP_RESPONSE" | jq '{id,status,next_action}'

Creating a top-up intent does not credit the wallet. The response gives your application the next step for collecting payment:

  • For client_secret, pass next_action.client_secret to the processor’s checkout SDK in your app.
  • For redirect, send the user to next_action.redirect_url to complete hosted checkout.

Complete that checkout using the processor’s test flow. This is the part that requires a checkout integration beyond the terminal commands. The top-up guide explains the flow, and the testing guide covers sandbox payments.

WalletD credits the wallet after verified processor confirmation. A browser callback alone is not confirmation that the wallet has been funded. While testing, read the top-up status:

curl --fail-with-body -sS "$WALLETD_API/v1/topups/$TOPUP_ID" \
  -H "Authorization: Bearer $WALLETD_API_KEY"

Continue only when status is succeeded. Show a pending state for created or processing; a failed, expired, or amount-mismatched top-up has not funded the wallet.

The idempotency key identifies this one funding operation. Reuse it with the same request body when retrying after a timeout. A genuinely new top-up needs a new key, which your application should persist with its own operation record.

3. Read the spendable balance

Fetch Alice’s balances after the top-up succeeds:

curl --fail-with-body -sS "$WALLETD_API/v1/users/$ALICE_ID/balances" \
  -H "Authorization: Bearer $WALLETD_API_KEY"

For a fresh USD wallet after the $50.00 credit, the cash entry should look like this, assuming no other activity:

{
  "purpose": "cash",
  "commodity": "USD",
  "balance": 5000,
  "available": 5000,
  "held": 0
}

The endpoint returns an array. Select the entry for the purpose your screen displays rather than assuming the first entry is cash.

Use available as the spendable amount. balance is the accounting total, and held is money reserved by an authorization that has not yet been captured or voided. A wallet can have a positive balance and still lack enough available funds for a new payment.

Keep amounts as integers throughout your integration and format them for display using the returned commodity. See the money model for examples across currencies and account purposes.

4. Send money to Bob

With Alice funded and Bob created in the same tenant, send $15.00:

curl --fail-with-body -sS "$WALLETD_API/v1/transfers" \
  -H "Authorization: Bearer $WALLETD_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: wallet-guide-alice-bob-transfer-001" \
  -d "$(jq -n --arg sender "$ALICE_ID" --arg recipient "$BOB_ID" \
    '{from_user:$sender,to_user:$recipient,amount:1500,note:"Lunch"}')"

A successful 201 response means the transfer has completed and both balances reflect it. Bob receives 1500 minor units. Alice pays that amount plus any configured transfer fee, so use the returned fee when explaining the debit.

For example, with a fee of 10 minor units, Alice’s $50.00 becomes $34.90 and Bob receives $15.00. With no fee, Alice has $35.00 left. These figures assume the top-up credited $50.00 and neither wallet had other activity.

If the response is lost, retry the exact request with the same idempotency key. Generating a new key on every retry could create another transfer. Reusing a key with different parameters returns 409 idempotency_key_reuse. The idempotency guide covers retry behavior in detail.

5. Show transaction history

Use WalletD’s history endpoint to populate the wallet activity screen:

curl --fail-with-body -sS \
  "$WALLETD_API/v1/users/$ALICE_ID/transactions?limit=20" \
  -H "Authorization: Bearer $WALLETD_API_KEY"

Results are newest first and use cursor pagination. Show the transaction type, amount, and reference so users can distinguish a top-up from a transfer. Store returned operation IDs with your own records so support can connect an application action to its WalletD transaction.

Read Alice’s and Bob’s balances again to verify the transfer. Your app should display the API’s current balances rather than treating locally incremented or decremented numbers as the final record.

Connect webhooks to your application

Polling is useful while walking through the API. Webhooks let your backend react when a top-up settles or a transfer completes, including when the user has already closed the app.

Once you have a reachable HTTPS receiver, register it. Replace the example URL before running this command:

curl --fail-with-body -sS "$WALLETD_API/v1/webhook_endpoints" \
  -H "Authorization: Bearer $WALLETD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url":"https://your-app.example/webhooks/walletd",
    "event_filters":["topup.succeeded","topup.failed","topup.amount_mismatch","transfer.completed"]
  }'

Save the returned signing secret securely when it is shown. Verify X-Wallet-Signature over the raw request body, check the timestamp, and deduplicate deliveries by event ID before applying application-side changes. Follow the webhook guide for the exact signature format and receiver examples.

A topup.succeeded notification should update your application’s status and prompt a balance refresh. It should not trigger a second credit: WalletD has already recorded the money movement.

Turn the walkthrough into your wallet experience

You now have the API flow for onboarding a user, funding their wallet, showing spendable funds, sending money, and displaying activity. Connect those operations to your application’s authenticated screens and keep operation IDs and idempotency keys in your backend records.

If your client needs direct WalletD access, your backend can exchange its API key for a short-lived user token through POST /v1/auth/user_tokens. Derive user_external_id from the authenticated session and request only the scopes the client needs. The authentication guide explains this exchange and user-level access restrictions.

Handle expected API decisions in the interface: insufficient_funds should prompt the user to review their balance, and tier_limit_exceeded should explain the account limit. For 429 responses, respect Retry-After; for transient server errors or timeouts, retry with backoff and the original idempotency key. See errors and limits for the response details.

From here, add merchant payments, payouts, or rewards as your product needs them. Each extends the wallet experience around the same foundation: authenticated operations, explicit money states, and a transaction history you can read back.

Accès anticipé

Devenez partenaire de conception.

Nous travaillons avec un petit nombre d'équipes qui construisent des produits de portefeuille et de paiement sur WalletD. Si c'est votre cas, parlons-en. Vous aurez un accès direct aux personnes qui l'ont conçu.