UMA Deposits and Withdrawals
Deposits and Withdrawals using the UMA (LNURL-p) standard on the Lightning Network including generating and sending to invoices.
Universal Money Address (UMA) is an open source project designed to make improvements over standard BOLT11 Lightning Network transactions.
These configurations will typically result in addresses taking one of the following formats:
- $[email protected] [Default]
- $satoshi@[platform].zerohash.id
- $satoshi@uma.[platform].com
- $satoshi@[platform].com
Note: The first release of UMA functionality supports invoice generation for the deposit of BTC which will be converted to fiat and sent via ACH/RTP. Additional functionality is listed under the “Upcoming Features” header below.
Register UMA Address
An invoice must be generated in order to receive a deposit over the Lightning Network. The customer can choose the username and the domain it is registered can take one of the forms listed above.
Once your UMA server is setup, a user must first be registered by your server and then sent to Zero Hash via POST /deposits/uma
{
"username": {UMA_ADDRESS}, // $username, see <https://uma.me> documentation
"participant_code": "{{participant_code}}"
}
Confirm Creation of UMA Address
The UMA address creation can be confirmed by calling GET /deposits/uma
This call will return a list of created addresses.
Update or Change UMA Address
An UMA address can be changed after creation, such as in the case of a typo, by calling PATCH /deposits/uma
{
"new_username": "$scdgcr2",
"previous_username":"$scdgcr",
"participant_code": "3QX004"
}
Success: 200
Error:
HTTP CODE 409 - conflict
{"message":"previous username does not match"}
Generate UMA Invoice
Once successfully registered, that platform can then generate an invoice by calling a LNURL request using a different Zero Hash URL depending on the environment (below example payloads use the CERT URL for illustration purposes):
- CERT: uma.cert.zerohash.com/.well-known/lnurlp
- PROD: uma.zerohash.com/.well-known/lnurlp
Note: The LNURL and PAYREQ must be sent from the onboarded Zero Hash platform. If the originating VASP is not the onboarded entity then these requests must be proxied to pass IP address / domain validation rules or else will be automatically rejected by default.
The LNURL request needs to follow the UMA documentation found here. A sample payload could look like the following:
GET uma.zerohash.com/.well-known/lnurlp/$bob?umaVersion=1.0&nonce=1234&vaspDomain=vasp1.com&signature=abcd&isSubjectToTravelRule=true×tamp=12345678
Note: The error message "UMA recipient not found" is returned if attempting to generate an invoice without registering the participant first.
The step following a successful LNURL request is to send the PayReq request with the generated invoice ID “lnurlp_callback”. The payreq call will look like the following:
{
"amount":"1000.USD",
"payerData":{
"identifier":"$[[email protected]](mailto:[email protected])",
"email":"[[email protected]](mailto:[email protected])",
"name":"payer",
"compliance":{
"kycStatus":"VERIFIED",
"utxos":\[
],
"nodePubKey":"",
"signature":"",
"signatureNonce":"",
"signatureTimestamp":,
"utxoCallback":""
}
},
"payeeData":{
"compliance":{
"mandatory":true
},
"identifier":{
"mandatory":true
},
"email":{
"mandatory":false
},
"name":{
"mandatory":false
}
},
"convert":"USD"
}
The invoice is then returned and ready to be paid.
USD Funds
In the initial MVP implementation, once the invoice is paid, the BTC is offramped to USD. Standard implementations will choose if the BTC will stay as BTC or be offramped as part of the transaction messages.
The USD funds will sit in the participant’s account until they are debited by the Real-Time Payment process outlined in the next section.
The following sections review calls that can be made to confirm each step of the process.
Confirming the Deposit
The following command will return the deposits to a participant code
GET /deposits?participant_code={{participant_code}}
{
"message": [
{
"settle_timestamp": 1718813440365,
"account_id": "31206b3e-4305-4b74-bf23-3d1eaec3ae00",
"participant_code": "7D1QCQ",
"account_group": "UKYI3K",
"asset": "BTC",
"amount": "0.00001558",
"reference_id": "Invoice:01903143-4368-52d6-0000-07acc1700390",
"source": "[email protected]",
"received_address": "$testaddress",
"account_label": "general",
"movement_id": "93f778b2-3620-493e-8c44-4468ab3483e3", ‘movement_id will be set to the payment_hash of the invoice which originated that deposit.
"run_id": "3848133"
}
],
"page": 1,
"page_size": 200,
"total_pages": 1,
"count": 1
}
Confirming Trade Details (BTC to USD)
The following command will return the trades for a participant code
GET /trades?participant_code={{participant_code}}
Confirm Participant Balance Increase
The following command will return the trades for a participant code
GET /accounts?account_owner={{participant_code}}
{
"message": [
{
"asset": "USD",
"account_owner": "7D1QCQ",
"account_type": "available",
"account_group": "UKYI3K",
"account_label": "general",
"balance": "1",
"account_id": "1058112a-b1e4-4142-9fe2-47cd2284bb99",
"last_update": 1718813461541
}
],
"page": 1,
"total_pages": 1
}
Confirm Participant Transaction Movements
The following command will return the movement of both digital assets and fiat balances for a given participant code
GET /accounts/{{account_id}}/movements
{
"message": [
{
"asset": "USD",
"account_owner": "7D1QCQ",
"account_type": "available",
"account_group": "UKYI3K",
"account_label": "general",
"balance": "1",
"account_id": "1058112a-b1e4-4142-9fe2-47cd2284bb99",
"last_update": 1718813461541
}
],
"page": 1,
"total_pages": 1
}
Transaction Limits and Controls (UMA)
UMA deposits and withdrawals are only available with VASPs that have completed Zero Hash’s Compliance UMA Due Diligence and have been added to the allowlist as part of that process. UMA transactions to/from external VASPs will be blocked until they are part of the allowlist.
The minimum allowed transaction amount using UMA is 1 satoshi and millisatoshis are not recognized because these are below the minimum precision amount.
Upcoming features
Below are a list of features related to UMA Transactions that are on the roadmap for a future release:
- Universal Money Address (UMA) Domain Registration
- Until this feature is live, custom domains must be setup with the Zero Hash team directly
- Universal Money Address (UMA) Withdrawals [Mid-September]
- The ability to fulfill invoices and send funds to UMA addresses
ACH and Real-Time Payments (RTP)
Please see the separate guide for ACH and Real-time Payments that will cover steps to withdraw fiat funds from the participant account to an external bank account.
Error Codes & States
Please see the following table of error states and courses of action
Error | Zero Hash Action | Platform Action |
---|---|---|
API to initiate offramp fails | Fail every htlc composing the invoice, funds revert to sender. No error is currently returned in this case. | Sending VASP would need to retry transaction due to canceled invoice |
API to initiate offramp succeeded but later payment status webhook comes back as payment failed | ZH notifies the platform via webhook of the failure. There is no automated retry of this step. | Platform to retry POST /payments |
When UMA address is incorrect → valid format $[email protected] | uma-server responds with bad request response and a proper error message. Resubmit with proper format | When UMA address is not found (non-existent) uma-server responds with bad request response and a proper error message. |
When any of the following query params is not sent: - umaVersion * only version 1.0 will be supported for now - nonce - vaspDomain - signature - isSubjectToTravelRule - Timestamp | uma-server responds with a bad request response and a proper error message. | |
When request goes OK | uma-server responds with StatusOK and the following data preferred currency estimated exchange rate compliance data vasp status: (should always be verified) | |
When request sends a version other than 1.0 (not supported) | uma-server responds with a bad request response and a proper error message. | |
Requesting the callback url multiple times | Should not be valid, resulting in bad request the second time the same callback url is used. | |
Sending payReq with not mandatory payer info | Should result in invalid request | |
receivingCurrencyCode is set to any value other than USD. | When sender-vasp sends a payreq with receivingCurrencyCode set to MSat, it should result in a rejection | |
Conversion is OK according to payreq response. | The conversion rate of the payreq response should be OK. Example: Request: { SendingCurrency: USD ReceivingCurrency: USD Amount: 10000 // means a rfq by total of $100 USD Notional Response: invoice with amount in MSats. Check if the value actually corresponds to 100 USD given the conversion rate exposed in the response | |
Verify response | Only USD, BTC and SAT should be supported currencies |
Updated 12 days ago