Authentication
Use mode-isolated, least-privilege Bearer keys for Merchant API requests.
Merchant API routes live under /api/v1. Send the raw API key in the Authorization header on every request:
Authorization: Bearer pk_test_... # or pk_live_...
Never put a secret key in checkout JavaScript, a mobile application, a URL, analytics, or source control. Call Ulasend from your server and keep each key in a secret manager.
Test and live mode isolation
| Key prefix | Mode | Behavior |
|---|---|---|
pk_test_ | Test | Mock receive addresses and simulated payments; never touches real money. |
pk_live_ | Live | Real receive addresses and blockchain payments. |
The credential's mode always wins. A pk_test_ key cannot create, list, or mutate live objects by sending livemode: true. A live key cannot read test objects by changing a query parameter. Test and live idempotency keys also occupy separate namespaces.
Scopes
Every key has explicit scopes. A :write scope also satisfies the matching :read permission. Create separate keys for separate services, and grant only what that service needs.
| Resource | Scopes | Included by default? |
|---|---|---|
| Invoices | invoices:read, invoices:write | Yes |
| Payment links | payment_links:read, payment_links:write | Yes |
| Products | products:read, products:write | Yes |
| Transactions | transactions:read | Yes |
| Balance | balance:read | Yes |
| Payouts | payouts:read, payouts:write | Read only |
| Payout wallets | payout_wallets:read | Read only |
| Settings | settings:read, settings:write | Read only |
| Webhooks | webhooks:read, webhooks:write | No—grant explicitly |
A missing permission returns 403 and names the required scope. Money movement and security configuration should use dedicated keys, not the broad key used by a storefront. Bearer-key webhook write access is limited to test endpoints; live webhook changes are dashboard-only.
Key management
Create, list, and revoke keys from an authenticated Dashboard session. These operations are restricted to organization owners/admins; an API key cannot create a successor key or revoke recovery access. Browser mutations require same-origin requests, and creating a live key requires an AAL2/MFA session.
The raw key is returned once. Ulasend stores only its prefix and SHA-256 hash. If a key is exposed, revoke it, create a replacement with the minimum scopes, deploy the replacement, and review its last-used timestamp and audit trail.
Idempotency for create requests
Send a unique Idempotency-Key on retryable create requests:
Idempotency-Key: wc-order-10482-create-v1
Keys must contain 8–255 printable characters. Ulasend binds the key to the merchant, mode, operation, and canonical request body for 24 hours.
- An identical retry returns the stored response with
Idempotency-Replayed: true. - Reusing a key with a different request body returns
409. - If the original request is still running, the duplicate returns
409 idempotency_in_progressandRetry-After: 1.
Generate the key from your own immutable operation id, not from the amount alone. A good pattern is <platform>-<order-id>-<operation>-v1. Retrying an ambiguous timeout with the same key is safe; changing the order should use a new operation version.
Typical storefront key
A custom site, Shopify adapter, or WooCommerce adapter that creates one invoice per order normally needs only:
invoices:write transactions:read
Add webhooks:write only to a separate test setup key when automating test endpoint configuration. Live endpoint setup and secret rotation stay in the owner/admin Dashboard flow.