Wallets
Add organisation wallets and prove you own them, add beneficiary wallets, and know which wallet a transaction can send from or pay to
There are two kinds of wallet, created through different endpoints:
| Organisation wallet | Beneficiary wallet | |
|---|---|---|
| Belongs to | Your organisation | A receiver |
| Used as | The wallet you send crypto from, and where on-ramp tokens arrive | The wallet you pay crypto to |
| Create | POST /organisations/{organisationId}/add-wallet | POST /wallets |
| Screening | Yes. The result is stored on the wallet | Yes. The wallet is refused unless it passes |
| Ownership proof | Required | Not required |
| Read | GET /organisations/{id}, in wallets | GET /receivers/{id}, in wallets |
| Remove | DELETE /organisations/{organisationId}/delete-wallet/{walletId} | DELETE /wallets/{id} |
Get network ids from GET /networks/client-visible. Wallets are supported on EVM networks and Solana.
Organisation wallets
Requirements
organisationIdmust be the user's selected organisation. Read it fromselectedOrganisation._idinGET /users/me.- The user must be a member of the organisation who is not view-only.
- The address must not already belong to another organisation.
Add the wallet
POST /organisations/{organisationId}/add-wallet
| Field | Required | Notes |
|---|---|---|
address | Yes | EVM: 0x followed by 40 hex characters. Solana: a base58 address |
network | Yes | Network _id. The address format must match the network |
type | No | EOA (default) or SAFE. SAFE is for Safe multisig wallets on EVM networks only |
ownershipMessage | No | The text you will sign to prove ownership. If you leave it out, the server generates a 6-digit number |
{
"address": "0x2f4e1b7c9a0d3e5f6a8b1c2d3e4f5a6b7c8d9e0f",
"network": "6650a1b2c3d4e5f607182930",
"type": "EOA",
"ownershipMessage": "Skyline wallet verification 2026-09-28"
}The response is the organisation, not the wallet. The new wallet is the last element of its wallets array. Keep its _id and ownership.message.
| Status | Message |
|---|---|
| 400 | No selected organisation |
| 400 | User selected organisation different from provided organisation Id |
| 401 | Your user does not have enough permission to perform this action |
| 400 | The address <address> is already linked to an existing organization |
| 400 | An address that is not valid for the network, or an unknown network |
Screening result
Every wallet is screened when you add it. The result is in walletAnalysisStatus on the wallet:
| Status | Meaning |
|---|---|
APPROVED | Passed. The wallet can be used |
REQUIRES_MANUAL_CHECK | Skyline compliance must review it. It cannot be used until approved |
BLOCKED | Refused. It cannot be used |
ERROR | Screening could not complete. Contact Skyline |
PENDING | Not screened yet |
Screening finishes during the add request. REQUIRES_MANUAL_CHECK, BLOCKED and ERROR change only after a Skyline review, so re-read the organisation later instead of polling quickly.
Prove ownership
A new organisation wallet starts with ownership.status = PENDING. To approve it:
- Take the exact
ownership.messagestring from the wallet. - Sign it with the wallet's key:
- EVM (EOA): EIP-191
personal_sign, which is whatsignMessagedoes in ethers, viem and web3.js. Send the0xhex signature. Contract wallets that implement EIP-1271 are also accepted. - Solana: an ed25519 signature over the UTF-8 bytes of the message, encoded in base58.
- EVM (EOA): EIP-191
- Send it to
POST /wallets/{id}/verify-wallet, where{id}is the wallet_id:
{
"message": "Skyline wallet verification 2026-09-28",
"signedMessage": "0x5b1c…"
}On success the response is the wallet with ownership.status = APPROVED.
| Status | Message | Cause |
|---|---|---|
| 400 | Invalid message | message is not exactly ownership.message |
| 400 | Invalid signature: signed by 0xabc…, expected 0xdef… | An EVM signature from a different address |
| 400 | Invalid signature | The signature does not verify |
| 400 | Wallet network is not configured | The wallet has no network |
| 403 | User <id> does not have permission to access this wallet. | The wallet is not in your organisation |
| 404 | Wallet not found | Wrong id |
Safe multisig wallets
A Safe approves the message once enough owners have signed it.
-
In the Safe app or with the Safe SDK, have an owner sign
ownership.messageas a Safe message. This creates a message proposal that other owners confirm. -
Call
POST /wallets/{id}/verify-walletwith themessageand the owner's signature. -
While the proposal has fewer confirmations than the Safe's threshold, the call returns 400 with
"code": "SAFE_SIGNATURES_PENDING". If the Safe service cannot be reached, it returns 503 with"code": "SAFE_SERVICE_UNAVAILABLE". -
Poll
POST /wallets/{id}/verify-wallet/recheck(no body) about every 10 seconds while the other owners confirm. It returns{ "status": …, "confirmations": …, "confirmationsRequired": … }:statusMeaning PENDING_SIGNATURESBelow the threshold. Keep polling APPROVEDThreshold met. The wallet now has ownership.status=APPROVED. StopNOT_FOUNDNo proposal for ownership.messageyet. Check step 1SERVICE_ERRORThe Safe service could not be reached. Retry later NOT_APPLICABLENot a Safe wallet on an EVM network, or ownership is already decided
Beneficiary wallets
POST /wallets
| Field | Required | Notes |
|---|---|---|
receiver | Yes | The receiver's _id |
address | Yes | The wallet address |
network | Yes | Network _id |
type | No | EOA (default) or SAFE |
{
"receiver": "665f1c2e9b1e8a0012a3b4c5",
"address": "0x8a3b9c2d1e0f4a5b6c7d8e9f0a1b2c3d4e5f6a7b",
"network": "6650a1b2c3d4e5f607182930"
}- The wallet is screened during the request. If it does not pass, the call returns 403
Wallet with address <address> is not approvedand nothing is saved. - If screening cannot complete, the wallet is saved with
walletAnalysisStatus=ERRORand cannot be used until Skyline reviews it. - The server does not check that the address format matches the network. Check it yourself: a wrong pair cannot be paid.
- No ownership proof is needed for a third party's wallet.
- The response is the wallet.
PATCH /wallets/{id} changes only network. DELETE /wallets/{id} removes a beneficiary wallet. Do not use it for organisation wallets.
Which wallet a transaction can use
| Role in a transaction | Wallet | Must have |
|---|---|---|
Sender in crypto-to-fiat and crypto-to-crypto (walletAddress) | An organisation wallet of the selected organisation | walletAnalysisStatus = APPROVED |
| Destination of an on-ramp | An organisation wallet | walletAnalysisStatus = APPROVED and ownership.status = APPROVED |
| Destination of a crypto payment | A beneficiary wallet | walletAnalysisStatus = APPROVED. A wallet on your organisation's own receiver also needs ownership.status = APPROVED |
Errors you may see at submit, accept or payout:
- 403
Wallet not found: the sender address is not an organisation wallet of your selected organisation. - 403 with
"code": "WALLET_NOT_WHITELISTED": the destination has not passed screening, or its ownership is not approved where required. The body includeswalletAnalysisStatus,ownershipStatusandownershipRequired.
Prove ownership of every organisation wallet as soon as you add it.
What's next
With receivers, bank accounts and wallets in place, create a transaction.
Bank Accounts
Add a bank account to a receiver for fiat payouts, with the fields the server checks per payment rail, Brazil PIX, errors and list behaviour
Transactions
The shared shape of every transaction, its statuses, the preconditions to accept, the money fields in the response, and the common errors