Quick Start
Get your first wallet created and receive a test deposit in under 5 minutes.
Prerequisites
- A HasaPay account (sign up)
- Your API key and secret key from the dashboard's API Keys page
- A REST client or shell with
curl
Step 1: Sign up and verify email
- Register at dashboard.hasapay.com/register
- Enter your organization name, email, and password
- Check your email for a 6-digit verification code
- Enter the code to verify
After verification, head to the API Keys page in the dashboard and generate your first key. Save the secret key immediately โ it's only shown once.
Step 2: Pick an auth tier
HasaPay has three auth tiers:
| Tier | Headers | Use when |
|---|---|---|
| JWT | Authorization: Bearer <token> |
Logged in via the dashboard, hitting user/org/team/api-key routes |
| HMAC | X-API-Key, X-Signature, X-Timestamp, X-Request-ID |
Server-to-server, hitting wallet/address writes or sends |
| Dual-auth | Either of the above | Every read endpoint and most config writes |
For this quickstart we'll use HMAC to create a wallet (HMAC-only) and read it back (dual-auth).
Full signing details in Authentication.
Step 3: Generate an HMAC signature
The signed payload is {timestamp}:{requestId}:{body} โ colon-joined, no method/path.
Node.js
const crypto = require('crypto');
const { randomUUID } = require('crypto');
function signRequest(secretKey, body) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const requestId = randomUUID();
const bodyString = body ? JSON.stringify(body) : '';
const payload = `${timestamp}:${requestId}:${bodyString}`;
const signature = crypto
.createHmac('sha256', secretKey)
.update(payload)
.digest('hex');
return { timestamp, requestId, signature, bodyString };
}
Python
import hmac
import hashlib
import json
import time
import uuid
def sign_request(secret_key, body):
timestamp = str(int(time.time()))
request_id = str(uuid.uuid4())
body_string = json.dumps(body, separators=(',', ':')) if body else ''
payload = f'{timestamp}:{request_id}:{body_string}'
signature = hmac.new(
secret_key.encode(),
payload.encode(),
hashlib.sha256,
).hexdigest()
return timestamp, request_id, signature, body_string
Sign the bytes you send. If you serialize JSON for the signature and let your HTTP library re-serialize differently, the signatures won't match.
Step 4: Create your first master wallet
BODY='{"chain":"ethereum","network":"sepolia","label":"My First Wallet"}'
TS=$(date +%s)
RID=$(uuidgen)
SIG=$(echo -n "$TS:$RID:$BODY" | openssl dgst -sha256 -hmac "$SECRET_KEY" | cut -d' ' -f2)
curl -X POST https://api.hasapay.com/api/v1/wallets \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-H "X-Signature: $SIG" \
-H "X-Timestamp: $TS" \
-H "X-Request-ID: $RID" \
-d "$BODY"
Response
{
"data": {
"id": "uuid",
"chain": "ethereum",
"network": "sepolia",
"address": "0xd502b72b8D969D60D1094174b1457C73671eb9d8",
"label": "My First Wallet",
"is_active": true,
"child_count": 0,
"created_at": "2026-06-09T10:00:00Z"
}
}
๐ You have a master wallet. Save the id โ you'll need it for the next step.
Step 5: Generate a deposit address
Path note: create is
/address(singular). Listing is/addresses(plural).
WALLET_ID="<from step 4>"
BODY='{"external_user_id":"cust_123","label":"Customer-001","metadata":{"order_id":"order_456"}}'
TS=$(date +%s)
RID=$(uuidgen)
SIG=$(echo -n "$TS:$RID:$BODY" | openssl dgst -sha256 -hmac "$SECRET_KEY" | cut -d' ' -f2)
curl -X POST "https://api.hasapay.com/api/v1/wallets/$WALLET_ID/address" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-H "X-Signature: $SIG" \
-H "X-Timestamp: $TS" \
-H "X-Request-ID: $RID" \
-d "$BODY"
Response
{
"data": {
"id": "uuid",
"master_wallet_id": "uuid",
"address": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
"chain": "ethereum",
"network": "sepolia",
"external_user_id": "cust_123",
"metadata": {"order_id": "order_456"},
"derivation_index": 1,
"is_active": true,
"auto_sweep_enabled": true,
"created_at": "2026-06-09T10:05:00Z"
}
}
The external_user_id and metadata will surface on every webhook for transactions to this address.
Step 6: Get test tokens and send some in
For testnet, free faucets:
| Chain | Faucet |
|---|---|
| Ethereum Sepolia | https://sepoliafaucet.com |
| Polygon Amoy | https://faucet.polygon.technology |
| Base Sepolia | https://faucet.base.org |
| BSC Testnet | https://testnet.bnbchain.org/faucet-smart |
| Tron Shasta | https://www.trongrid.io/faucet |
Send a small amount of native token (and any test stablecoin you've enabled via POST /assets/enable) to your child address. Within a couple of blocks you'll see the deposit appear in the dashboard, and a deposit.pending โ deposit.confirmed webhook if you've subscribed.
Step 7: Set up a webhook (optional)
curl -X POST https://api.hasapay.com/api/v1/webhooks \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/hasapay",
"events": ["deposit.confirmed", "withdrawal.completed", "withdrawal.failed"]
}'
The webhook endpoint accepts dual-auth, so JWT works fine. Use your organization's webhook secret to verify X-HasaPay-Signature on incoming deliveries โ see Webhooks.
Next steps
- Authentication โ full HMAC/JWT/dual-auth reference
- API Reference Overview โ every endpoint
- Webhooks โ payload shapes, signature verification, retries
- Transactions โ sending and monitoring
Troubleshooting
401 invalid_signature
- Sign the exact body bytes you send on the wire โ no whitespace drift between signing and sending
- Use Unix seconds, not milliseconds, for
X-Timestamp - The signed payload is
{timestamp}:{requestId}:{body}, notMETHOD\nPATH\nTIMESTAMP\nBODY
401 timestamp_expired
- System clock is drifting; sync it
- Don't cache a timestamp โ mint it at request time
409 duplicate_request
- You reused an
X-Request-ID. Mint a fresh UUID per request, including retries