SEP: 0024
Title: Hosted Deposit and Withdrawal
Author: SDF
Status: Active
Created: 2019-09-18
Updated: 2024-08-07
Version 3.7.1
This SEP defines the standard way for anchors and wallets to interact on behalf of users. This improves user experience by allowing wallets and other clients to interact with anchors directly without the user needing to leave the wallet to go to the anchor's site. It is based on SEP-0006, but only supports the interactive flow, and cleans up or removes confusing artifacts. If you are updating from SEP-0006 see the changes from SEP-6 at the bottom of this document.
This proposal defines a standard protocol enabling the following features directly within a wallet or other Stellar client:
- Deposit external assets with an anchor
- Withdraw assets from an anchor
- Communicate deposit & withdrawal fee structure for an anchor to the user
- Handle anchor KYC needs, including handling KYC info within an interactive webapp hosted by the anchor
- Check the status of ongoing deposits or withdrawals involving the user
- View history of deposits and withdrawals involving the user
To support this protocol an anchor acts as a server and implements the specified REST API endpoints, while a wallet implements a client that consumes the API. The goal is interoperability, so a wallet implements a single client according to the protocol, and will be able to interact with any compliant anchor. Similarly, an anchor that implements the API endpoints according to the protocol will work with any compliant wallet.
- The assets issuing account should have a home_domain set so the clients can find the toml file to begin the process.
- An anchor must define the location of their
TRANSFER_SERVER_SEP0024
in theirstellar.toml
. This is how a wallet knows where to find the anchor's server. - Anchors and clients must support SEP-10 web authentication to enable authenticated deposits, withdrawals, or transaction history lookups.
- If the Anchor supports SEP-38,
ANCHOR_QUOTE_SERVER
must be defined in theirstellar.toml
.
POST /transactions/deposit/interactive
POST /transactions/withdraw/interactive
GET /info
GET /fee
: deprecated optional (only needed for complex fee structures).SEP-38 GET /price
endpoint should be used to communicate fees and exchange rates.GET /transactions
GET /transaction
As stated, Anchors must support SEP-10 web authentication to enable authenticated deposits, withdrawals,
or transaction history lookups. Clients must submit the JWT previously obtained from the anchor via the
SEP-10 authentication flow to all API endpoints that provide user data. /info
should be
unauthenticated, but all other endpoints will require a token.
The JWT should be included in all requests as request header:
Authorization: Bearer <JWT>
In the case of the interactive webapp, since the client cannot add the authorization header, we recommend passing a short-lived JWT via URL query parameters and then using your own backend session scheme for the rest of the interactive flow. Query parameters can leak, so its important to have this JWT be one-time use, or at least short-lived.
Client applications can use a single Stellar account to hold multiple users' funds. To distinguish users that use the
same Stellar account, the decoded SEP-10 JWT's sub
value may contain a memo value after the Stellar account
(G...:2810101841641761712
) OR the sub
value will be a Muxed Account (M...
). The anchor
should use this sub
attribute in their data model to identify unique users.
This document will refer to these accounts as shared accounts. See the SEP-10 Memos and Muxed Accounts sections for more information.
Note that Stellar accounts are either shared or they are not. This means anchors should ensure that a Stellar account previously authenticated with a memo should not be authenticated later without a memo. Conversely, an account that was previously authenticated without a memo should not be later authenticated as a shared account.
Note that both the source account of a withdrawal payment and the destination account of a deposit can be different than the account authenticated via SEP-10.
This protocol was originally designed to provide conversions between off-chain and on-chain equivalent assets – for
instance BRL <> BRLT
, USD <> USDC
, NGN <> NGNT
, etc. – but it has been updated to make exchanges between off-chain
and on-chain non-equivalent assets like BRL <> USDC
.
To support the exchange of non-equivalent assets using this protocol, the anchor must allow users to select the off-chain asset they would like to provide or receive (for deposit and withdrawal, respectively) within the anchor's interactive flow. The anchor should then display an exchange rate to the user in addition to any fees charged for facilitating the transaction. Whether this exchange rate is binding or an estimate is up to the anchor, but this distinction must be communicated to the user.
Finally, anchors should populate the optional attributes listed in the GET /transaction(s)
endpoints related to
non-equivalent asset transactions, specifically amount_in_asset
, amount_fee_asset
, and amount_out_asset
. This
allows client applications can relay this information, in addition to the amounts specified in the associated
attributes, back to their users.
Alternatively, the Anchor can support the SEP-38 RFQ API and the quote_id
attribute in
POST /transactions/deposit/interactive
and POST /transactions/withdraw/interactive
requests. This enables wallets to
check the Anchor's exchange rates prior to initiating a transaction and obtain a firm quote to use for an upcoming
transaction.
To convert at the market rate, clients get an
indicative quote with SEP-38 and then proceed to calling
the POST /transactions/withdraw/interactive
or POST /transactions/deposit/interactive
endpoints. Market rate conversions executed in this way do not provide a guarantee of the final price to be paid for the
asset. Here is how this kind of conversion should happen in deposits and withdrawals for an asset pair like
BRL <> USDC
:
Considering the user will be making a bank deposit of fiat BRL
in exchange for USDC
in the Stellar network:
- The Wallet checks the assets' market conversion rate with
SEP-38 GET /price
and shows the estimated price to the user. - The Wallet initiates a
POST /transactions/deposit/interactive
using the parameterasset_code
. - The user proceeds with the off-chain deposit, sending
BRL
to the Anchor's bank account. - Upon receiving the off-chain deposit funds, the Anchor converts it to
USDC
using the market rate and sends the resulting amount to the user's Stellar account.
Considering the user will be making a Stellar payment of USDC
in exchange for withdrawing BRL
in the bank account:
- The Wallet checks the assets' market conversion rate with
SEP-38 GET /price
and shows the estimated price to the user. - The Wallet initiates a
POST /transactions/withdraw/interactive
using the parameterasset_code
. - The user proceeds with the on-chain payment, sending
USDC
to the Anchor's Stellar account. - Upon receiving the on-chain withdraw funds, the Anchor converts it to
BRL
using the market rate and sends the resulting amount to the user's bank account.
With firm quotes, users are guaranteed an asset price they will pay if they submit the payment and anchor receives the
payment before the quote expiration. Here is how the conversion should happen in deposits and withdrawals for an asset
pair like USDC <> BRL
:
Considering the user will be making a bank deposit of fiat BRL
in exchange for USDC
in the Stellar network:
- The Wallet gets a firm conversion rate in
SEP-38 POST /quote
and shows the price and expiration date to the user. - The Wallet initiates a
POST /transactions/deposit/interactive
using the parameterquote_id
returned from the previous step, as well asasset_code
. - The user proceeds with the off-chain deposit, sending
BRL
to the Anchor's bank account before the quote expires. - Upon receiving the off-chain deposit funds, the Anchor converts it to
USDC
according with the agreed quote and sends the resulting amount to the user's Stellar account. - If the user sends the funds after the quote expiration, the Anchor can proceed according with its own policy.
Considering the user will be making a Stellar payment of USDC
in exchange for withdrawing BRL
in her bank account:
- The Wallet gets a firm conversion rate in
SEP-38 POST /quote
and shows the price and expiration date to the user. - The Wallet initiates a
POST /transactions/withdraw/interactive
using the parameterquote_id
returned from the previous step, as well asasset_code
. - The user proceeds with the on-chain payment, sending
USDC
to the Anchor's Stellar account before the quote expires. - Upon receiving the on-chain withdraw funds, the Anchor converts it to
BRL
according with the agreed quote and sends the resulting amount to the user's bank account. - If the user sends the funds after the quote expiration, the Anchor can proceed according with its own policy.
Valid CORS headers are necessary to allow web clients from other sites to use the endpoints. The following HTTP header must be set for all transfer server responses, including error responses.
Access-Control-Allow-Origin: *
In order for browsers-based wallets to validate the CORS headers, as specified by W3C, the preflight request (OPTIONS request) must be implemented in all the endpoints that support Cross-Origin.
This protocol involves the transfer of value, and so HTTPS is required for all endpoints for security. Wallets and anchors should refuse to interact with any insecure HTTP endpoints.
Note that the signature discussed here is only for URL callbacks and does not apply to postMessage
callbacks
This protocol involves the optional use of URL callbacks that the Anchor can issue to update the wallet on status
of a
transaction. In order to validate the integrity and provenance of the callback, the Anchor MUST include a signature in
the HTTP Header Signature
or X-Stellar-Signature
(deprecated).
Wallets should support both headers until the X-Stellar-Signature
header is removed from the specification.
These headers MUST follow the specification: t=<timestamp>, s=<base64 signature>
where:
- timestamp is the current Unix timestamp (number of seconds since epoch) at the time the callback is sent. This is used to assure the freshness of the request and to prevent this request to be replayed in the future.
- base64 signature is the base64 encoding of the request signature. We explain below how to compute and verify this
signature. The signature is computed using the Stellar private key linked to the
SIGNING_KEY
field of the anchor'sstellar.toml
. Note that the timestamp and the Wallet hostname will be part of the signature to prevent replay and relay attacks.
It is the wallet's responsibility to:
- Verify the signature using the corresponding Stellar
SIGNING_KEY
field of the anchor'sstellar.toml
. - Verify the freshness of the request by comparing the
timestamp
in the request with the current timestamp at the time of the reception and discard every request above a threshold of few seconds (1 or 2 minute(s) maximum). - Send a working callback URL (parameter
callback
oron_change_callback
) to the anchor.
- Check that callback request has
Signature
orX-Stellar-Signature
(deprecated) header - Parse the header and extract:
- Key
t
: timestamp - Key
s
: base64 signature
- Key
- Verify the request freshness: current timestamp - timestamp < few seconds (1-2 minute(s) max)
- Extract the body of the request
- Base64 decode the base64 signature to get the signature
- Prepare the payload to verify the signature:
- The timestamp (as a string)
- The character
.
- The wallet host to send the callback request to
- The character
.
- The body
- Verify the signature using the correct
SIGNING_KEY
- Prepare the callback
- Prepare the payload to sign:
- Current timestamp (as a string)
- The character
.
- The wallet host to send the callback request to
- The character
.
- The callback request body
- Sign the payload
<timestamp>.<host>.<body>
using the Anchor private key - Base64 encode the signature
- Build the
Signature
orX-Stellar-Signature
(deprecated) header:Signature: t=<current timestamp>, s=<base64 encoded signature>
X-Stellar-Signature: t=<current timestamp>, s=<base64 encoded signature>
All endpoints accept in requests the following Content-Type
s:
multipart/form-data
application/x-www-form-urlencoded
application/json
multipart/form-data
is only necessary when including binary data type values from SEP-9. Any of the
above encoding schemes may be used when requests do not include binary data.
All endpoints respond with content type:
application/json
SEP-24 lays out many options for how deposit and withdrawal can work. These are recommendations for getting a wallet or anchor implementation working with minimal effort while providing a great user experience.
Note: Both wallets and anchors should implement a sandbox mode for testing that uses the Stellar testnet and fake banking rails so counterparties can run through the flow without a need to collaborate.
- Identify anchors you want to support manually, and test them with your wallet to be sure they work before allowing them to be used with your wallet. We encourage you to support as many anchors as possible.
- For each anchor, use information from its
stellar.toml
file and its/info
endpoint to display useful information to the user about the asset they've picked. - Provide a UI that allows users to pick an asset, anchor, and amount to use for deposit or withdraw. The UI should
display the asset's quote (if possible) as well as information such as the address of the anchor and description of
the asset from the
stellar.toml
file. - If SEP-38 quote API is supported by the anchor
- Fetch the asset's exchange rate at the market price using
SEP-38 GET /price
API. - For the firm quote, use
SEP-38 POST /quote
API and show the quote and expiration time to the user.
- Fetch the asset's exchange rate at the market price using
- Authentication
- Perform authentication via SEP-10 before hitting those endpoints
- Make a request to
/transactions/deposit/interactive
or/transactions/withdraw/interactive
.- This will respond with the interactive url needed to proceed with KYC and deposit/withdraw details.
- For
/transactions/deposit/interactive
and/transactions/withdraw/interactive
- Optionally attach any fields from SEP-9 as POST parameters in the
/deposit
or/withdraw
endpoints in order to let the anchor pre-fill them in the interactive flow UI. This is optional, but can create a much nicer user experience.email_address
,first_name
andlast_name
are good examples of fields to help pre-fill the anchors website. - Optionally attach SEP-38 quote id, in order to the anchor to use the quote previously shown to the user.
- Optionally attach any fields from SEP-9 as POST parameters in the
- For
/transactions/deposit/interactive
- Handle the interactive flow, handle it as described in detail.
- Handle the special cases, they're relatively common.
- For
/transactions/withdraw/interactive
- Handle the interactive flow, handle it as described in detail.
- When the transaction status becomes
pending_user_transfer_start
send the required payment as described in the interactive webapp callback or the/transaction
endpoint. This can be apayment
orpath_payment
operation. Sending payments viaaccount_merge
orcreate_account
is not supported at this time. - Some wallets might exchange currencies only once they're ready to send the withdrawal payment, so exchange rate
fluctuations might require withdrawal values to slightly vary from the originally provided
amount
. Anchors are instructed to accept a variation of ±10% between the informedamount
and the actual value sent to the anchor's Stellar account. The withdrawn amount will be adjusted accordingly.
- Transaction history
- Provide a list of historical and current deposits and withdrawals at the
/transactions
endpoint for wallets to show a view of all of a single anchors transactions in a list. - Provide status or instructions for a specific deposit or withdraw at the
/transaction
endpoint
- Provide a list of historical and current deposits and withdrawals at the
- Provide a full-featured implementation of
/info
. - Implement SEP-38 to offer better user experience. Wallet would be able to show rates and fees your anchor provides early in the process. (Before the user initiates a transaction via interactive UI)
- For both deposit and withdrawal:
- To start an interactive flow, provide the Customer info needed response.
- Some wallets may include other fields indicated in SEP-9 as POST parameters added to the
/transactions/deposit/interactive
or/transactions/withdraw/interactive
endpoints. These can be stored and used to pre-populate fields in the interactive flow. This is optional for the wallet to provide and optional for anchors to respect, but it does create a much nicer user experience. - Some wallets may include
quote_id
fetched from theSEP-38 POST /quote
API. If provided, your implementation must respect the agreed quote. - Include the
id
field in your response to/transactions/deposit/interactive
and/transactions/withdraw/interactive
so the wallet can check up on the status of the transaction if it wants. - Also include the
id
field in the popup URL you provide to the wallet. This allows you to keep track of the transaction when the user visits the URL. - We recommend you use SEP-10 for authentication in the interactive flow and do not separately prompt for password to achieve a good user experience (although asking for MFA when confirming a transaction or requiring email confirmation is reasonable). Putting a one time use token or quickly expiring JWT in the URL returned to the client is a good way to keep continuity between authenticated API calls and fresh interactive flow requests.
- Test your interactive flows on mobile. They should be easy to use on all devices: make them responsive, handle auto-fill well, and do smart keyboard management on mobile devices.
- Interactive deposit
- Your interactive deposit popup will do everything needed to initiate the deposit without the user needing to interact with the wallet further. It should either directly initiate the deposit, or handle displaying information (such as reference number or bank account number) that the user will need to complete their deposit.
- Interactive withdrawal
- Your withdrawal flow will have to pass control back to the user's wallet, so it can initiate the withdrawal with a
Stellar payment to your withdrawal address. You'll need to communicate the withdrawal address, amount and status to
the wallet using the
callback
parameter, and also by making it available on your/transaction
endpoint. See details for polling by the wallet. - In order to fulfill a withdrawal, a wallet must make a payment to the Stellar address that the anchor provides. It
is the anchor's job to watch for Stellar payments to the given address and make the external transaction as soon as
they're detected. Anchors must listen for
payment
andpath_payment
operations. Most Stellar SDKs already support listening to all payment forms via streaming. - When the anchor detects the fulfilling payment from the wallet and expects the customer to pick up the withdrawn
amount of cash, the transaction status must be changed to
pending_user_transfer_complete
to indicate the withdrawn amount is ready for pick-up. - Some wallets might exchange currencies only once they're ready to send the withdrawal payment, so there might be
slight fluctuations of value between the informed withdrawal
amount
and the actual transferred amount. It is recommended for anchors to accept an amount fluctuation of up to ±10%, and adjust the amount to be transferred ( and fees) to reflect the actual value received.
- Your withdrawal flow will have to pass control back to the user's wallet, so it can initiate the withdrawal with a
Stellar payment to your withdrawal address. You'll need to communicate the withdrawal address, amount and status to
the wallet using the
- Providing transaction status
- Provide the
/transaction
endpoint. The wallet relies on it to complete interactive withdrawals. - Provide the
/transactions
endpoint. Wallets normally display transaction histories.
- Provide the
A deposit is when a user sends some non-stellar asset (BTC via Bitcoin network, USD via bank transfer, Cash to a teller, etc...) to an account held by an anchor. In turn, the anchor sends an equal amount of tokens on the Stellar network (minus fees) to the user's Stellar account.
If the anchor supports SEP-38 quotes, it can also provide a bridge between non-equivalent tokens. For example, the anchor can receive ARS via bank transfer and in return send the equivalent value (minus fees) as USDC on the Stellar network to the user's Stellar account.
The deposit endpoint allows a wallet to get deposit information from an anchor, so a user has all the information needed to initiate a deposit. It also lets the anchor specify additional information that the user must submit interactively via a popup or embedded browser window to be able to deposit.
After a successful deposit request has been made, a transaction record with the id
provided in the response should be
retreivable from GET /transaction(s)
. This transaction must be in the incomplete
status until the user has provided
the anchor all information necessary for the transaction to be completed once received off-chain.
If the given account does not exist, or if the account doesn't have a trustline for that specific asset, see the Special Cases section below.
POST TRANSFER_SERVER_SEP0024/transactions/deposit/interactive
The fields below should be placed in the request body using the multipart/form-data
encoding.
Request Parameters:
Name | Type | Description |
---|---|---|
asset_code |
string | The code of the stellar asset the user wants to receive for their deposit with the anchor. The value passed must match one of the codes listed in the /info response's deposit object. When quote_id is specified, asset_code must match the quote's buy_asset asset code. |
asset_issuer |
string | (optional) The issuer of the stellar asset the user wants to receive for their deposit with the anchor. If asset_issuer is not provided, the anchor should use the asset issued by themselves as described in their TOML file. If native is specified as the asset_code , asset_issuer must be not be set. When quote_id is specified, this parameter must match the quote's buy_asset asset code or be omitted. |
source_asset |
string in Asset Identification Format | (optional) The asset user wants to send. Note, that this is the asset user initially holds (off-chain or fiat asset).If this is not provided, it will be collected in the interactive flow. When quote_id is specified, this parameter must match the quote's sell_asset asset code or be omitted. |
amount |
number | (optional) Amount of asset requested to deposit. If this is not provided it will be collected in the interactive flow. When qoute_id is specified, this parameter must match the quote's quote.sell_amount or be omitted. |
quote_id |
string | (optional) The id returned from a SEP-38 POST /quote response. If this parameter is provided and the user delivers the deposit funds to the Anchor before the quote expiration, the Anchor must respect the conversion rate agreed in that quote. When quote_id is set, asset_code , source_asset and amount must be validated by the anchor, if present. In case of a conflict with the ones used to create the SEP-38 quote, this request should be rejected with a 400 . |
account |
G... or M... string |
(optional) The Stellar or muxed account the client wants to use as the destination of the payment sent by the anchor. Defaults to the account authenticated via SEP-10 if not specified. |
memo_type |
string | (optional) Type of memo that anchor should attach to the Stellar transaction, one of text , id or hash . |
memo |
string | (optional) Value of memo to attach to transaction, for hash this should be base64-encoded. Because a memo can be specified in the SEP-10 JWT for Shared Accounts, this field can be different than the value included in the SEP-10 JWT. For example, a client application could use the value passed for this parameter as a reference number used to match payments made to account . |
wallet_name |
string | (deprecated,optional) In communications / pages about the deposit, anchor should display the wallet name to the user to explain where funds are going. However, anchors should use client_domain (for non-custodial) and sub value of JWT (for custodial) to determine wallet information. |
wallet_url |
string | (deprecated,optional) Anchor should link to this when notifying the user that the transaction has completed. However, anchors should use client_domain (for non-custodial) and sub value of JWT (for custodial) to determine wallet information. |
lang |
string | (optional) Defaults to en if not specified or if the specified language is not supported. Language code specified using RFC 4646 which means it can also accept locale in the format en-US . error fields in the response, as well as the interactive flow UI and any other user-facing strings returned for this transaction should be in this language. |
claimable_balance_supported |
boolean | (optional) True if the client supports receiving deposit transactions as a claimable balance, false otherwise. |
customer_id |
string | (optional) id of an off-chain account (managed by the anchor) associated with this user's Stellar account (identified by the JWT's sub field). If the anchor supports [SEP-12], the customer_id field should match the [SEP-12] customer's id. customer_id should be passed only when the off-chain id is know to the client, but the relationship between this id and the user's Stellar account is not known to the Anchor. |
When anchor receives a request, it can determine an origin of the request. First, it should check client_domain
value
of the authentication token. If it's set, this wallet is an origin of the request. Otherwise, sub
value of the JWT is
the origin of the request. For custodial wallets, anchor should store internal mapping between wallet keys and wallet
domains. Note, that custodial wallets should only use 1 authentication key, so it's easy to map them.
Additionally, any SEP-9 parameters may be passed as well to make the onboarding experience simpler.
When uploading data for fields specified in SEP-9, binary
type fields (typically files) should be
submitted after all other fields. The reason for this is that some web servers require binary
fields at the end so
that they know when they can begin processing the request as a stream.
Example:
POST /transactions/deposit/interactive
Content-Type: application/x-www-form-urlencoded
asset_code=USD&[email protected]&account=GACW7NONV43MZIFHCOKCQJAKSJSISSICFVUJ2C6EZIW5773OU3HD64VI
There are several possible kinds of response, depending on whether the anchor needs more information about the user, how it should be sent to the anchor, and if there are any errors.
Responses are detailed in the Deposit and Withdraw shared responses section below.
If the given Stellar account
does not exist on receipt of the deposit funds, the anchor should use the CreateAccount
operation to create the account with at least enough XLM for the minimum reserve and a trust line to the requested asset
(2.01 XLM is recommended). To indicate that account creation is not supported, set the account_creation
attribute
within GET /info
's features
object to false
, otherwise clients will assume account creation is supported.
The anchor can add this minimal funding amount to the service fee, but this requires calculating the worth of the minimum funding amount in units of the requested asset.
Since the anchor doesn't have the user account's secret key, the user must create a trust line to the anchor's asset before the anchor can send the requested asset tokens to the user's account. The anchor should listen for the user to establish this trust line. Once the trust line is there, the anchor should send the requested asset tokens to the account on Stellar to complete the deposit.
If the anchor does not support creating new accounts for users and account
doesn't exist yet, the anchor should return
a 400 Bad Request
error in the deposit response. The response body should be a JSON object containing an
error
field that explains why the request failed.
The deposit flow can only be fulfilled if the Stellar account
has established a trust line for the given asset. To
ensure this is accomplished, when initiating the deposit flow, Wallet
should check if the account
has a trust line
for the given asset. If it doesn't:
Wallet
checks ifaccount
has enough XLM to create a trust line. If it does, skip to step4
.- If
account
doesn't have enough XLM,Wallet
starts listening for transactions to the givenaccount
, waiting for it to have enough XLM for a trust line. - When asked for a deposit,
Anchor
detects ifaccount
has enough XLM to create a trust line. If it doesn't,Anchor
sends the needed amount of XLM to theAccount
for creating a trust line.Anchor
may charge a service fee to cover the cost of the XLM, but this must be communicated to the user. Anchor
then starts listening for trust line creations for thataccount
.Wallet
detects the arrival of XLM in theaccount
, and establishes a trust line.Anchor
detects the trust line creation in theaccount
. If the asset isAUTH_REQUIRED
,Anchor
approves the new trust line.Anchor
proceeds with the deposit flow.
Claimable Balances are an optional feature that enable payments to accounts that do not have a trustline for the asset being deposited. This feature splits a payment into two separate parts: the creation of a balance, and the claiming of a balance. A claimable balance can be claimed by the designated claimant (user) after it has been created by the anchor.
Using this feature, anchors will no longer have to wait until the user's Stellar account has a trustline to the asset before sending funds. Instead, anchors can make the payment using a CreateClaimableBalance operation and the user's Stellar account can claim the funds at their own convenience using a ClaimClaimableBalance operation.
NOTE: Supporting this feature will be made mandatory in the future. Therefore, it is highly recommended for wallets to implement this functionality now.
Wallets: To support claimable balances wallets must
- Send the additional
claimable_balance_supported
request parameter in the POST/transactions/deposit/interactive
request body. - Periodically poll for account's available claimable balances.
- Provide a UI that allows users to claim claimable balances.
Anchors: To support claimable balances anchors must
- Set the
claimable_balances
attribute withinGET /info
'sfeatures
object totrue
- Accept the
claimable_balance_supported
request parameter inPOST /transactions/deposit/interactive
requests - Submit deposit transactions using
CreateClaimableBalance
operations to Stellar accounts that don't yet have a trustline to the asset requested. - Add the
claimable_balance_id
attribute to their depositGET /transaction(s)
responses.
Anchors and Wallets: Both anchors and wallets still must support the aforementioned Stellar account doesn't trust asset flow. Wallets need to be interoperable with anchors that have not adopted the claimable balance feature and vice versa.
- Make a request to
/transactions/deposit/interactive
and provide theclaimable_balance_supported
request parameter. - Listen for anchor's callback or poll the transaction endpoint [
/transaction
] for the transaction status. - When the transaction status becomes
pending_user_transfer_start
, the user must send the required payment as described in the interactive webapp callback or the/transaction
endpoint. - If the anchor doesn't support claimable balances, the anchor's callback or
/transaction(s)
endpoint will contain thepending_trust
status. In this case, use the flow described above. - Otherwise, detect the
claimable_balance_id
value populated in the anchor's/transaction(s)
endpoint or poll Horizon's /claimable_balances endpoint for outstanding claimable balances. When a claimable balance is detected using either method, the transaction status should becompleted
. - Claim the balance using the value via the
ClaimClaimableBalance
operation. See the "Claiming Claimable Balances" section to learn more about how to claim a balance.
In order to claim a balance of an asset, the Stellar accounts must establish a trustline to the asset. Adding a trustline only needs to happen once per asset sent.
Below is an example of how to claim a claimable balance. Omitted from the example is the Change Trust operation required if the Stellar account does not have a trustline.
const transaction = new TransactionBuilder(account, {
fee: 100,
networkPassphrase: this.network_passphrase,
})
.addOperation(Operation.claimClaimableBalance({ balanceId }))
.setTimeout(0)
.build();
transaction.sign(keypair);
const result = await this.server.submitTransaction(transaction);
- Wallets make a request to
/transactions/deposit/interactive
providing theclaimable_balance_supported
request parameter. - Anchors update their internal database record of the transaction to indicate the wallet supports receiving a claimable balance.
- Users send the external asset to the anchor's off-chain account.
- Anchors detect that the user's Stellar account doesn't have a trustline.
- Anchors submits a Stellar transaction containing a claimable balance operation.
- Anchors update the
/transaction(s)
attriutesstatus
tocompleted
andclaimable_balance_id
to the ID returned in the Horizon response.
Predicates are one of the
claimable balance parameters used to craft
a Claimable Balance transaction. They are conditions that must be satisfied in order for the recipient to claim the
balance. Anchors are free to set whichever predicates they feel are necessary in order to claim the balance. If there
are no predicate preferences, UNCONDITIONAL
allows accounts to claim balances at anytime.
This operation allows a user to redeem an asset currently on the Stellar network for the real asset (BTC, USD, stock, etc...) via the anchor of the Stellar asset.
If the anchor supports SEP-38 quotes, it can provide a withdraw that makes a bridge between non-equivalent tokens by receiving, for instance USDC from the Stellar network and in return sending the equivalent value (minus fees) as NGN to the user's bank account.
The withdraw endpoint allows a wallet to get withdrawal information from an anchor, so a user has all the information needed to initiate a withdrawal. It also lets the anchor specify the url for the interactive webapp to continue with the anchor's side of the withdraw.
After a successful withdraw request has been made, a transaction record with the id
provided in the response should be
retreivable from GET /transaction(s)
. Unless the
no additional information needed response is returned, this transaction
must be in the incomplete
status until the user has provided the anchor all information necessary for the transaction
to be completed once received on-chain.
POST TRANSFER_SERVER_SEP0024/transactions/withdraw/interactive
The fields below should be placed in the request body using the multipart/form-data
encoding.
Request parameters:
Name | Type | Description |
---|---|---|
asset_code |
string | Code of the asset the user wants to withdraw. The value passed must match one of the codes listed in the /info response's withdraw object. When quote_id is specified, asset_code must match the quote's sell_asset asset code. native is a special asset_code that represents the native XLM token. |
asset_issuer |
string | (optional) The issuer of the stellar asset the user wants to withdraw with the anchor. If asset_issuer is not provided, the anchor should use the asset issued by themselves as described in their TOML file. If native is specified as the asset_code , asset_issuer must be not be set. When quote_id is specified, asset_issuer must match the quote's buy_asset asset issuer or be omitted. |
destination_asset |
string in Asset Identification Format | (optional) The asset user wants to receive. It's an off-chain or fiat asset. If this is not provided, it will be collected in the interactive flow. When quote_id is specified, this parameter must match the quote's buy_asset asset code or be omitted. |
amount |
number | (optional) Amount of asset requested to withdraw. If this is not provided it will be collected in the interactive flow. When qoute_id is specified, this parameter must match the quote's quote.sell_amount or be omitted. |
quote_id |
string | (optional) The id returned from a SEP-38 POST /quote response. If this parameter is provided and the user delivers the deposit funds to the Anchor before the quote expiration, the Anchor must respect the conversion rate agreed in that quote. When quote_id is set, asset_code , destination_asset and amount must be validated by the anchor, if present. In case of a conflict with the ones used to create the SEP-38 quote, this request should be rejected with a 400 . |
account |
G... or M... string |
(optional) The Stellar or muxed account the client will use as the source of the withdrawal payment to the anchor. Defaults to the account authenticated via SEP-10 if not specified. |
memo |
string | (deprecated, optional) This field was originally intended to differentiate users of the same Stellar account. However, the anchor should use the sub value included in the decoded SEP-10 JWT instead. Anchors should still support this parameter to maintain support for outdated clients. See the Shared Account Authentication section for more information. |
memo_type |
string | (deprecated, optional) Type of memo . One of text , id or hash . Deprecated because memos used to identify users of the same Stellar account should always be of type of id . |
wallet_name |
string | (deprecated,optional) In communications / pages about the withdrawal, anchor should display the wallet name to the user to explain where funds are coming from. However, anchors should use client_domain (for non-custodial) and sub value of JWT (for custodial) to determine wallet information. |
wallet_url |
string | (deprecated,optional) Anchor can show this to the user when referencing the wallet involved in the withdrawal (ex. in the anchor's transaction history). However, anchors should use client_domain (for non-custodial) and sub value of JWT (for custodial) to determine wallet information. |
lang |
string | (optional) Defaults to en if not specified or if the specified language is not supported. Language code specified using RFC 4646 which means it can also accept locale in the format en-US . error fields in the response, as well as the interactive flow UI and any other user-facing strings returned for this transaction should be in this language. |
refund_memo |
string | (optional) The memo the anchor must use when sending refund payments back to the user. If not specified, the anchor should use the same memo used by the user to send the original payment. If specified, refund_memo_type must also be specified. |
refund_memo_type |
string | (optional) The type of the refund_memo . Can be id , text , or hash . See the memos documentation for more information. If specified, refund_memo must also be specified. |
customer_id |
string | (optional) id of an off-chain account (managed by the anchor) associated with this user's Stellar account (identified by the JWT's sub field). If the anchor supports [SEP-12], the customer_id field should match the [SEP-12] customer's id. customer_id should be passed only when the off-chain id is know to the client, but the relationship between this id and the user's Stellar account is not known to the Anchor. |
Additionally, any SEP-9 parameters may be passed as well to make the onboarding experience simpler.
Example:
POST TRANSFER_SERVER_SEP0024/transactions/withdraw/interactive
Content-Type: application/x-www-form-urlencoded
asset_code=USD&[email protected]&account=GACW7NONV43MZIFHCOKCQJAKSJSISSICFVUJ2C6EZIW5773OU3HD64VI
When uploading data for fields specified in SEP-9, binary
type fields (typically files) should be
submitted after all other fields. The reason for this is that some web servers require binary
fields at the end so
that they know when they can begin processing the request as a stream.
There are several possible kinds of response, depending on whether the anchor needs more information about the user, how it should be sent to the anchor, and if there are any errors.
Responses are detailed in the Deposit and Withdraw shared responses section below.
Response code: 200 OK
An anchor that requires the user to fill out information on a webpage hosted by the anchor should use this response. This can happen in situations where the anchor needs KYC information about a user, or when the anchor needs the user to perform a custom step for each transaction like entering an SMS code to confirm a withdrawal or selecting a bank account. A wallet that receives this response should open a popup browser window or embedded webview to the specified URL. The anchor must take care that the popup page displays well on a mobile device, as many wallets are phone apps.
As the user is interacting with the anchor popup, they will make progress on their deposit or withdrawal and cause
updates to the transaction status. The wallet must either listen for a callback or poll the
/transaction
endpoint for updates about the transaction from the anchor. This allows
the wallet to show the user status information and confirm if the deposit attempt initiated successfully or failed. For
withdrawals, the wallet must get information on where to send the withdrawal payment to the anchor.
The response body must be a JSON object with the following fields:
Name | Type | Description |
---|---|---|
type |
string | Always set to interactive_customer_info_needed . |
url |
string | URL hosted by the anchor. The wallet should show this URL to the user as a popup. |
id |
string | The anchor's internal ID for this deposit / withdrawal request. The wallet will use this ID to query the /transaction endpoint to check status of the request. |
Example response:
{
"type": "interactive_customer_info_needed",
"url": "https://api.example.com/kycflow?account=GACW7NONV43MZIFHCOKCQJAKSJSISSICFVUJ2C6EZIW5773OU3HD64VI",
"id": "82fhs729f63dh0v4"
}
Before the wallet sends the user to the url
field received from the anchor, it may add query parameters to the URL.
The basic parameters are summarized in the table below.
Name | Type | Description |
---|---|---|
callback |
string | (optional) postMessage or a URL that the anchor should POST a JSON message to when the user successfully completes the interactive flow. If the callback is a URL (not a postMessage ), it needs to be signed by the anchor and the signature needs to be verified by the wallet according to the callback signature specification. |
on_change_callback |
string | (optional) postMessage or a URL that the anchor should POST a JSON message to when the status or kyc_verified properties change. If the callback is a URL (not a postMessage ), it needs to be signed by the anchor and the signature needs to be verified by the wallet according to the callback signature specification. |
The URL supplied by both callback parameters should receive the full transaction object.
callback
details
If the wallet wants to be notified that the user has completed the anchor's interactive flow (either success or
failure), it can add this parameter to the URL. If the user abandons the process, the anchor does not need to report
anything to the wallet. If the callback
value is a URL, the anchor must POST
to it with a JSON message as the body.
postMessage
details
Note that there are some security concerns associated with supporting postMessage
callbacks. These conerns are
detailed in the section below.
If provided, the anchor must post a JSON message (either as a JSON-serialized string or a plain javascript object) to
window.opener
via the Javascript
Window.postMessage
method. If window.opener
is undefined, the message must be posted to window.parent
instead.
Because callback
is used to notify the wallet that the interactive flow is complete, it is common for anchors to make
the postMessage
callback as a result of the user clicking a "Close" button or similar UI element. The wallet can then
close the window displaying the anchor's interactive flow using
[Window.close()
](https://developer.mozilla.org/en-US/docs/Web/API/Window/close). This ensures the wallet application
receives the transaction's information and the user has a smooth experience.
Security Concerns (noopener
, postMessage
)
Wallet applications display content rendered by a third party anchor service. It is recommended to use the
rel=noopener
attribute on links or the
noopener
feature for
Window.open()
.
Note that using noopener
prohibits the use of postMessage
callbacks. If postMessage
callbacks are required for
your implementation, it recommended to only open URLs from anchors that you, the wallet developer, trust.
To validate the provenance and integrity of the callback, anchors must sign it and wallet servers must validate the signature before taking any action.
Differences between callback
and on_change_callback
Anchors may make at most one request containing a /transaction
response body to callback
when the user is finished
with the interactive flow.
on_change_callback
should be called each time the status
or kyc_verified
properties of the transaction change. It
can be called any number of times.
callback
/ on_change_callback
example
The JSON message should be identical to the response format for the /transaction endpoint.
// Example callback at the end of an interactive withdraw, indicating that the anchor is waiting for the wallet to send a payment in the amount of 80 of the asset in question.
fetch(callback, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
// a plain javascript object can also be used for postMessage calls
transaction: {
id: "anchors_identifier_for_this_transaction",
status: "pending_user_transfer_start",
withdraw_anchor_account: "ANCHORS_STELLAR_ACCOUNT_ID",
withdraw_memo: "MEMO_ANCHOR_EXPECTS_TO_SEE",
withdraw_memo_type: "text|hash|id",
amount_in: "80",
// ... Any other values from the /transaction endpoint can and should be passed as well
},
}),
});
After the user has provided all necessary information to the anchor through the interactive popup (url
), the anchor
should either close the popup automatically or instruct the user to close it themselves. If the anchor needs to provide
instructions to the user for sending funds to it's off-chain account (in the deposit case), the anchor should not close
the popup until the user has indicated to do so.
When the popup is closed, the transaction's status should no longer be incomplete
. Instead, it should be one of the
following:
pending_anchor
if the anchor must verify KYC information given prior to receiving funds.pending_user
if the user must take some action before sending funds to the anchor.pending_user_transfer_start
if anchor is ready to receive the funds. This is the most common status afterincomplete
.pending_user_transfer_complete
if anchor detects the payment is fulfilled and the funds is ready for the user to pick up. This is the most common status afterpending_user_transfer_start
if the user must pick up the funds in person.pending_trust
if the user does not have a trustline to the asset requested for deposit. Note that a trustline can be established after the anchor has received funds. A trustline is not required if both the wallet and anchor support Claimable Balances.
Given the nature of the interactive withdrawal, the user will interact with the anchor via the popup. They enter information needed to complete the withdrawal like destination bank account or KYC details. Once the anchor has enough information to know how to complete the withdrawal, the anchor closes the popup and the wallet allows the user to complete the withdrawal inside the wallet's app. It has to work this way because the wallet must transfer the correct amount of the withdrawal asset to the anchor's Stellar account before the anchor can complete its end of the withdrawal.
The wallet needs to wait for the transaction's status to be pending_user_transfer_start
before sending a payment on
Stellar to the anchor's account. To detect the transaction's status, either poll the /transaction
endpoint with the
id
provided in the /transactions/withdraw/interactive
response from the anchor until the necessary information is
available, or register a callback with the anchor as described above.
When a successful response comes back (either from polling or via callback), the response will contain the transaction fields described in the /transactions endpoint.
The wallet must use the response fields in the following way to complete the withdrawal:
status
:pending_user_transfer_start
means the user has given all necessary info to the anchor, and it is up to the wallet to send the actual stellar assets.status
:pending_user_transfer_complete
means the Stellar payment has been successfully received by the anchor and the off-chain funds are available for the customer to pick up. Instructions to pick up the funds should be provided to the customer either through themore_info_url
url or another method such as email.withdraw_anchor_account
: send the withdrawal payment to this Stellar account.withdraw_memo
: (if specified) use this memo in the payment transaction to the anchor.withdraw_memo_type
: use this as the memo type.amount_in
: the amount expected in the Stellar payment.
The next step is for the wallet to display a confirmation screen summarizing the withdrawal to the user, and then send a
Stellar payment to withdraw_anchor_account
. The wallet should show the following info to the user:
to
: show the user what external account they will be withdrawing to.external_extra_text
: show the bank name or store name that the user will be withdrawing their funds to.more_info_url
: tell the user they can visit this URL for more info about their transaction as it processes.
The anchor may chose to replace most of the digits in the to
account number with *
s to keep it confidential.
When a user initiates a deposit, the wallet must kick off a background process to handle the case where the account has no trustline.
After that, the wallet displays the anchor's interactive URL in a popup, and everything else the user needs to do to complete that deposit either happens in the popup, or externally (for example by initiating a SEPA transfer). The wallet must track the status of the deposit in the same fashion as described in the withdrawal guidance section, and may show that information to the user.
If the wallet displays information to the user, it can display any of the fields that may be useful to the user, such as
more_info_url
, status
, and amount_in
.
Response code: 403 Forbidden
This endpoint requires authentication.
{
"type": "authentication_required"
}
Every other HTTP status code will be considered an error. The body should contain a string indicating the error details. This error is in a human readable format in the language indicated in the request and is intended to be displayed by the wallet. For example:
{
"error": "This anchor doesn't support the given currency code: ETH"
}
Allows an anchor to communicate basic info about what their TRANSFER_SERVER_SEP0024
supports to wallets and clients.
If an anchor supports the native asset, the native
asset code with a null
asset issuer should be included in the
response.
The native
asset on the Stellar Public Network is the lumen (XLM).
GET TRANSFER_SERVER_SEP0024/info
Request parameters:
Name | Type | Description |
---|---|---|
lang |
string | (optional) Defaults to en if not specified or the if the specified language is not supported. Language code specified using RFC 4646 which means it can also accept locale in the format en-US . description fields in the response should be in this language. |
The response should be a JSON object like:
{
"deposit": {
"USD": {
"enabled": true,
"fee_fixed": 5,
"fee_percent": 1,
"min_amount": 0.1,
"max_amount": 1000
},
"ETH": {
"enabled": true,
"fee_fixed": 0.002,
"fee_percent": 0
},
"native": {
"enabled": true,
"fee_fixed": 0.00001,
"fee_percent": 0
}
},
"withdraw": {
"USD": {
"enabled": true,
"fee_minimum": 5,
"fee_percent": 0.5,
"min_amount": 0.1,
"max_amount": 1000
},
"ETH": {
"enabled": false
},
"native": {
"enabled": true
}
},
"fee": {
"enabled": false
},
"features": {
"account_creation": true,
"claimable_balances": true
}
}
The JSON object contains an entry for each Stellar asset that the anchor supports for deposit and/or withdrawal.
enabled
:true
if deposit for this asset is supportedmin_amount
: Optional minimum amount. No limit if not specified.max_amount
: Optional maximum amount. No limit if not specified.fee_fixed
: Optional fixed (base) fee for deposit. In units of the deposited asset. This is in addition to anyfee_percent
. Omit if there is no fee or the fee schedule is complex.fee_percent
: Optional percentage fee for deposit. In percentage points. This is in addition to anyfee_fixed
. Omit if there is no fee or the fee schedule is complex.fee_minimum
: Optional minimum fee in units of the deposited asset.
enabled
:true
if withdrawal for this asset is supportedmin_amount
: Optional minimum amount. No limit if not specified.max_amount
: Optional maximum amount. No limit if not specified.fee_fixed
: Optional fixed (base) fee for withdraw. In units of the withdrawn asset. This is in addition to anyfee_percent
.fee_percent
: Optional percentage fee for withdraw in percentage points. This is in addition to anyfee_fixed
.fee_minimum
: Optional minimum fee in units of the withdrawn asset.
If fee_fixed
or fee_percent
are provided, the total fee is calculated as
(amount * fee_percent) + fee_fixed = fee_total
. If the fee structure doesn't fit this model, omit them and provide the
/fee
endpoint instead.
An anchor should also indicate in the /info
response if they support the fee
endpoint, by providing the following
fields:
authentication_required
:true
if client must be authenticated before accessing thefee
endpoint.enabled
:true
if the endpoint is available.
The features
object contains boolean values indicating whether specific features are supported by the anchor. If the
object or specific feature is not present in the response, the default value described below may be assumed. This
information enables wallets to adjust their behavior based on the feature set supported by the anchor.
Name | Default | Description |
---|---|---|
account_creation |
true |
Whether or not the anchor supports creating accounts for users requesting deposits. |
claimable_balances |
false |
Whether or not the anchor supports sending deposit funds as claimable balances. This is relevant for users of Stellar accounts without a trustline to the requested asset. |
The default values for the features listed above have been selected based on the ecosystem's current support. It is highly recommend to support all features enumerated for the best user experience.
This endpoint is deprecated. The SEP-38 GET /price
endpoint should be used to fetch fees instead.
The fee endpoint allows an anchor to report the fee that would be charged for a given deposit or withdraw operation.
This is important to allow an anchor to accurately report fees to a user even when the fee schedule is complex. If a fee
can be fully expressed with the fee_fixed
, fee_percent
or fee_minimum
fields in the /info
response, then an
anchor should not implement this endpoint.
Note that this endpoint does not support providing fees for transactions using non-equivalent on and off-chain assets,
since it only accepts the on-chain asset_code
request parameter.
GET TRANSFER_SERVER_SEP0024/fee
Request parameters:
Name | Type | Description |
---|---|---|
operation |
string | Kind of operation (deposit or withdraw ). |
type |
string | (optional) Type of deposit or withdrawal (SEPA , bank_account , cash , etc...). |
asset_code |
string | Asset code. |
amount |
float | Amount of the asset that will be deposited/withdrawn. |
Example request:
GET https://api.example.com/fee?operation=withdraw&asset_code=ETH&amount=0.5
On success the endpoint should return 200 OK
HTTP status code and a JSON object with the following fields:
Name | Type | Description |
---|---|---|
fee |
float | The total fee (in units of the asset involved) that would be charged to deposit/withdraw the specified amount of asset_code . |
Example response:
{
"fee": 0.013
}
Every HTTP status code other than 200 OK
will be considered an error. The body should contain error details. For
example:
{
"error": "This anchor doesn't support the given currency code: ETH"
}
The transaction history endpoint helps anchors enable a better experience for users using an external wallet. With it, wallets can display the status of deposits and withdrawals while they process and a history of past transactions with the anchor. It's only for transactions that are deposits to or withdrawals from the anchor. It returns a list of transactions from the account encoded in the authenticated JWT.
If the decoded JWT's sub
parameter also contains a memo, the anchor must only return transactions for the user
identified by a combination of the account and memo. The anchor must not return all transactions for the Stellar account
because that would include transactions for other memos.
GET TRANSFER_SERVER_SEP0024/transactions
Request parameters:
Name | Type | Description |
---|---|---|
asset_code |
string | The code of the asset of interest. E.g. BTC, ETH, USD, INR, native, etc. |
no_older_than |
UTC ISO 8601 string | (optional) The response should contain transactions starting on or after this date & time. |
limit |
int | (optional) The response should contain at most limit transactions. |
kind |
string | (optional) The kind of transaction that is desired. Should be either deposit or withdrawal . |
paging_id |
string | (optional) The response should contain transactions starting prior to this ID (exclusive). |
lang |
string | (optional) Defaults to en if not specified or if the specified language is not supported. Language code specified using RFC 4646 which means it can also accept locale in the format en-US . |
On success the endpoint should return 200 OK
HTTP status code and a JSON object with the following fields:
Name | Type | Description |
---|---|---|
transactions |
array | List of transactions as requested by the client, sorted in time-descending order. |
Each object in the transactions
array should have the following fields:
Name | Type | Description |
---|---|---|
id |
string | Unique, anchor-generated id for the deposit/withdrawal. |
kind |
string | deposit or withdrawal . |
status |
string | Processing status of deposit/withdrawal. |
status_eta |
number | (optional) Estimated number of seconds until a status change is expected. |
kyc_verified |
boolean | (optional) True if the anchor has verified the user's KYC information for this transaction. |
more_info_url |
string | A URL that is opened by wallets after the interactive flow is complete. It can include banking information for users to start deposits, the status of the transaction, or any other information the user might need to know about the transaction. |
amount_in |
string | Amount received by anchor at start of transaction as a string with up to 7 decimals. Excludes any fees charged before the anchor received the funds. |
amount_in_asset |
string | (optional) The asset received or to be received by the Anchor. Must be present if the deposit/withdraw was made using non-equivalent assets. The value must be in SEP-38 Asset Identification Format. See the Asset Exchanges section for more information. |
amount_out |
string | (optional) Amount sent by anchor to user at end of transaction as a string with up to 7 decimals. Excludes amount converted to XLM to fund account and any external fees. This field should be set as soon as the anchor can calculate it using amount_in , the exchange rate between amount_in_asset and amount_out_asset , and fee.total . |
amount_out_asset |
string | (optional) The asset delivered or to be delivered to the user. Must be present if the deposit/withdraw was made using non-equivalent assets. The value must be in SEP-38 Asset Identification Format. See the Asset Exchanges section for more information. |
amount_fee |
string | (deprecated) Amount of fee charged by anchor. This field is deprecated in favor of the fee_details field. |
amount_fee_asset |
string | (deprecated, optional) The asset in which fees are calculated in. Must be present if the deposit/withdraw was made using non-equivalent assets. The value must be in SEP-38 Asset Identification Format. See the Asset Exchanges section for more information. |
fee_details |
object | Description of fee charged by the anchor. The schema for this object is defined in the Fee Details Object Schema section below. If quote_id is present, it should match the referenced quote's fee object. |
quote_id |
string | (optional) The ID of the quote used when creating this transaction. Should be present if a quote_id was included in the POST /transactions/deposit/interactive or POST /transactions/withdraw/interactive request. Clients should be aware that the quote_id may not be present in older implementations. |
started_at |
UTC ISO 8601 string | Start date and time of transaction. |
completed_at |
UTC ISO 8601 string | (optional) The date and time of transaction reaching completed or refunded status. |
updated_at |
UTC ISO 8601 string | (optional) The date and time of transaction reaching the current status. |
user_action_required_by |
UTC ISO 8601 string | (optional) The date and time by when the user action is required. In certain statuses, such as pending_user_transfer_start or incomplete , anchor waits for the user action and user_action_required_by field should be used to show the time anchors gives for the user to make an action before transaction will automatically be moved into a different status (such as expired or to be refunded ). user_action_required_by should only be specified for statuses where user action is required, and omitted for all other. Anchor should specify the action waited on using message or more_info_url . |
stellar_transaction_id |
string | transaction_id on Stellar network of the transfer that either completed the deposit or started the withdrawal. |
external_transaction_id |
string | (optional) ID of transaction on external network that either started the deposit or completed the withdrawal. |
message |
string | (optional) Human readable explanation of transaction status, if needed. |
refunded |
boolean | (deprecated, optional) This field is deprecated in favor of the refunds object and the refunded status. True if the transaction was refunded in full. False if the transaction was partially refunded or not refunded. For more details about any refunds, see the refunds object. |
refunds |
object | (optional) An object describing any on or off-chain refund associated with this transaction. The schema for this object is defined in the Refunds Object Schema section below. |
Name | Type | Description |
---|---|---|
deposit_memo |
string | (optional) This is the memo (if any) used to transfer the asset to the to Stellar address |
deposit_memo_type |
string | (optional) Type for the deposit_memo . |
from |
string | Sent from address, perhaps BTC, IBAN, or bank account. |
to |
string | Stellar address the deposited assets were sent to. |
claimable_balance_id |
string | (optional) ID of the Claimable Balance used to send the asset initially requested. |
Name | Type | Description |
---|---|---|
withdraw_anchor_account |
string | If this is a withdrawal, this is the anchor's Stellar account that the user transferred (or will transfer) their asset to. |
withdraw_memo |
string | Memo used when the user transferred to withdraw_anchor_account . Assigned null if the withdraw is not ready to receive payment, for example if KYC is not completed. |
withdraw_memo_type |
string | Memo type for withdraw_memo . |
from |
string | Stellar address the assets were withdrawn from |
to |
string | Sent to address (perhaps BTC, IBAN, or bank account in the case of a withdrawal, Stellar address in the case of a deposit). |
status
should be one of:
incomplete
-- there is not yet enough information for this transaction to be initiated. Perhaps the user has not yet entered necessary info in an interactive flow.pending_user_transfer_start
-- the user has not yet initiated their transfer to the anchor. This is the next necessary step in any deposit or withdrawal flow after transitioning fromincomplete
.pending_user_transfer_complete
-- the Stellar payment has been successfully received by the anchor and the off-chain funds are available for the customer to pick up. Only used for withdrawal transactions.pending_external
-- deposit/withdrawal has been submitted to external network, but is not yet confirmed. This is the status when waiting on Bitcoin or other external crypto network to complete a transaction, or when waiting on a bank transfer.pending_anchor
-- deposit/withdrawal is being processed internally by anchor. This can also be used when the anchor must verify KYC information prior to deposit/withdrawal.on_hold
-- deposit/withdrawal is currently on hold for additional checks after receiving user's funds. Anchor may use this status to indicate to the user that transaction is being reviewed (for example, for compliance reasons). Once this status cleared, transaction should follow the regular flow.pending_stellar
-- deposit/withdrawal operation has been submitted to Stellar network, but is not yet confirmed.pending_trust
-- the user must add a trustline for the asset for the deposit to complete.pending_user
-- the user must take additional action before the deposit / withdrawal can complete, for example an email or 2fa confirmation of a withdraw.completed
-- deposit/withdrawal fully completed.refunded
-- the deposit/withdrawal is fully refunded.expired
-- funds were never received by the anchor and the transaction is considered abandoned by the user. If a SEP-38 quote was specified when the transaction was initiated, the transaction should expire when the quote expires, otherwise anchors are responsible for determining when transactions are considered expired.no_market
-- could not complete deposit because no satisfactory asset/XLM market was available to create the account.too_small
-- deposit/withdrawal size less thanmin_amount
.too_large
-- deposit/withdrawal size exceededmax_amount
.error
-- catch-all for any error not enumerated above.
Name | Type | Description |
---|---|---|
amount_refunded |
string | The total amount refunded to the user, in units of amount_in_asset . If a full refund was issued, this amount should match amount_in . |
amount_fee |
string | The total amount charged in fees for processing all refund payments, in units of amount_in_asset . The sum of all fee values in the payments object list should equal this value. |
payments |
array | A list of objects containing information on the individual payments made back to the user as refunds. The schema for these objects is defined in the section below. |
Name | Type | Description |
---|---|---|
id |
string | The payment ID that can be used to identify the refund payment. This is either a Stellar transaction hash or an off-chain payment identifier, such as a reference number provided to the user when the refund was initiated. This id is not guaranteed to be unique. |
id_type |
string | stellar or external . |
amount |
string | The amount sent back to the user for the payment identified by id , in units of amount_in_asset . |
fee |
string | The amount charged as a fee for processing the refund, in units of amount_in_asset . |
Name | Type | Description |
---|---|---|
total |
string | The total amount of fee applied. |
asset |
string | The asset in which the fee is applied, represented through the Asset Identification Format. |
breakdown |
array | (optional) An array of objects detailing the fees that were used to calculate the conversion price. This can be used to datail the price components for the end-user. |
Name | Type | Description |
---|---|---|
name |
string | The name of the fee, for example ACH fee , Brazilian conciliation fee , Service fee , etc. |
description |
string | (optional) A text describing the fee. |
amount |
string | The amount of asset applied. If fee_details.breakdown is provided, sum(fee_details.breakdown.amount) should be equals fee_details.total . |
The following should hold true for all transaction records, assuming amount_in_asset
and amount_out_asset
are the
same. If they are different, the following should still hold true after converting all amounts to units of one of the
assets.
amount_out = amount_in - amount_fee - refunds.amount_refunded - refunds.amount_fee
refunds.amount_refunded = sum(refunds.payments[].amount)
refunds.amount_fee = sum(refunds.payments[].fee)
Example response:
{
"transactions": [
{
"id": "82fhs729f63dh0v4",
"kind": "deposit",
"status": "pending_external",
"status_eta": 3600,
"external_transaction_id": "2dd16cb409513026fbe7defc0c6f826c2d2c65c3da993f747d09bf7dafd31093",
"more_info_url": "https://youranchor.com/tx/242523523",
"amount_in": "18.34",
"amount_out": "18.24",
"amount_fee": "0.1",
"started_at": "2017-03-20T17:05:32Z",
"claimable_balance_id": null
},
{
"id": "82fhs729f63dh0v4",
"kind": "withdrawal",
"status": "completed",
"amount_in": "510",
"amount_out": "490",
"amount_fee": "5",
"started_at": "2017-03-20T17:00:02Z",
"completed_at": "2017-03-20T17:09:58Z",
"updated_at": "2017-03-20T17:09:58Z",
"more_info_url": "https://youranchor.com/tx/242523523",
"stellar_transaction_id": "17a670bc424ff5ce3b386dbfaae9990b66a2a37b4fbe51547e8794962a3f9e6a",
"external_transaction_id": "1941491",
"withdraw_anchor_account": "GBANAGOAXH5ONSBI2I6I5LHP2TCRHWMZIAMGUQH2TNKQNCOGJ7GC3ZOL",
"withdraw_memo": "186384",
"withdraw_memo_type": "id",
"refunds": {
"amount_refunded": "10",
"amount_fee": "5",
"payments": [
{
"id": "b9d0b2292c4e09e8eb22d036171491e87b8d2086bf8b265874c8d182cb9c9020",
"id_type": "stellar",
"amount": "10",
"fee": "5"
}
]
}
},
{
"id": "92fhs729f63dh0v3",
"kind": "deposit",
"status": "completed",
"amount_in": "510",
"amount_out": "490",
"amount_fee": "5",
"started_at": "2017-03-20T17:00:02Z",
"completed_at": "2017-03-20T17:09:58Z",
"updated_at": "2017-03-20T17:09:58Z",
"more_info_url": "https://youranchor.com/tx/242523526",
"stellar_transaction_id": "17a670bc424ff5ce3b386dbfaae9990b66a2a37b4fbe51547e8794962a3f9e6a",
"external_transaction_id": "1947101",
"refunds": {
"amount_refunded": "10",
"amount_fee": "5",
"payments": [
{
"id": "1937103",
"id_type": "external",
"amount": "10",
"fee": "5"
}
]
}
},
{
"id": "92fhs729f63dh0v3",
"kind": "deposit",
"status": "pending_anchor",
"amount_in": "510",
"amount_out": "490",
"amount_fee": "5",
"started_at": "2017-03-20T17:00:02Z",
"updated_at": "2017-03-20T17:05:58Z",
"more_info_url": "https://youranchor.com/tx/242523526",
"stellar_transaction_id": "17a670bc424ff5ce3b386dbfaae9990b66a2a37b4fbe51547e8794962a3f9e6a",
"external_transaction_id": "1947101",
"refunds": {
"amount_refunded": "10",
"amount_fee": "5",
"payments": [
{
"id": "1937103",
"id_type": "external",
"amount": "10",
"fee": "5"
}
]
}
}
]
}
Every HTTP status code other than 200 OK
will be considered an error. An empty transaction list is not an error. The
body should contain error details. For example:
{
"error": "This anchor doesn't support the given currency code: ETH"
}
The transaction endpoint enables clients to query/validate a specific transaction at an anchor.
Anchors must ensure that the SEP-10 JWT included in the request contains the Stellar account and optional memo value used when making the original deposit or withdraw request that resulted in the transaction requested using this endpoint.
GET TRANSFER_SERVER_SEP0024/transaction
Request parameters:
Name | Type | Description |
---|---|---|
id |
string | (optional) The id of the transaction. |
stellar_transaction_id |
(optional) string | The stellar transaction id of the transaction. |
external_transaction_id |
(optional) string | The external transaction id of the transaction. |
lang |
string | (optional) Defaults to en if not specified or if the specified language is not supported. Language code specified using RFC 4646 which means it can also accept locale in the format en-US . |
One of id
, stellar_transaction_id
or external_transaction_id
is required.
On success the endpoint should return 200 OK
HTTP status code and a JSON object with the following fields:
Name | Type | Description |
---|---|---|
transaction |
object | The transaction that was requested by the client. |
The transaction
object should be of the same form as the objects returned by the
TRANSFER_SERVER_SEP0024/transactions
endpoint.
Example response:
{
"transaction": {
"id": "82fhs729f63dh0v4",
"kind": "deposit",
"status": "pending_external",
"status_eta": 3600,
"external_transaction_id": "2dd16cb409513026fbe7defc0c6f826c2d2c65c3da993f747d09bf7dafd31093",
"more_info_url": "https://youranchor.com/tx/242523523",
"amount_in": "18.34",
"amount_out": "18.24",
"amount_fee": "0.1",
"started_at": "2017-03-20T17:05:32Z",
"claimable_balance_id": "00000000c2d8c89264288dbde8488364fd3fd30850fd4e7fbf6d1e9809702558afa4fdea"
}
}
If the transaction cannot be found, the endpoint should return a 404 NOT FOUND
result.
Every HTTP status code other than 200 OK
will be considered an error. An empty transaction list is not an error. The
body should contain error details. For example:
{
"error": "This anchor doesn't support the given currency code: ETH"
}
There is a small set of changes when upgrading from SEP-6 to SEP-24.
- SEP-24 now requires authentication on all endpoints. The
authentication_required
flag is removed from the/info
endpoint since authentication is assumed. GET /deposit
andGET /withdraw
have been replaced withPOST /transactions/deposit/interactive
andPOST /transactions/withdraw/interactive
. This is for security purposes to keep submitted personally identifiable information out of URL query parameters. Instead of passing data through query parameters, it's now expected throughmultipart/form-data
POST requests.- Remove the
type
parameter from the/deposit
and/withdraw
endpoints as this is information collected in the interactive flow. - Removed the
fields
andtypes
definitions from the/info
endpoint. Instead of trying to communicate to the wallet what fields an anchor wishes for the wallet to collect and send, the wallet can send any fields it wishes to share for a better UX (such asemail_address
), and anything not sent should be collected by the anchor. If none of these fields are sent, the anchor should still be able to continue and collect these fields during the interative flow. - Removed
external_extra
transaction property since this should all live in a human readablemore_info_url
. - Changed the response of the deposit and withdraw endpoints from 403 to 200 since this is the expected flow.
/transactions
and/transaction
are now required endpoints.- It is now recommended to use a short-lived, one-time JWT in the context of the interactive webapp.
- Anchors should not accept JWT's that have expired or been used before.
/transactions
endpoint no longer accepts anaccount
query. The account is pulled from the JWT.
- iOS and macOS SDK: https://github.com/Soneso/stellar-ios-mac-sdk/blob/master/README.md#6-anchor-client-interoperability
- Demo wallet client for development of an anchor server: https://sep24.stellar.org
- Anchor server implementation example and reuseable app in Python: https://github.com/stellar/django-polaris
- Solar wallet: https://solarwallet.io
v3.7.1
Makeamount_out
optional in transaction responses(#1520)v3.7.0
Adduser_action_required_by
field to transaction responses (#1484)v3.6.0
Add newon_hold
status (#1479)v3.5.0
Addfee_details
field to the transaction object #1429v3.4.0
: Addcustomer_id
field to deposit and withdrawal requests (#1410)v3.3.0
: Add support for SEP-38 API. Add optionalquote_id
parameter topost /transactions/deposit/interactive
andPOST /transactions/deposit/interactive
requests. Addquote_id
to the transaction object schema. Deprecate/fee
endpoint. (#1358)v3.2.0
: Deprecate thewallet_name
andwallet_url
parameters ofPOST /transactions/deposit/interactive
andPOST /transactions/withdrawal/interactive
requests (#1364)v3.1.0
: Add native asset support to SEP-24 (#1363)v3.0.0
: Make theaccount
parameter forPOST /transactions/deposit/interactive
request optional (#1343)v2.9.2
: Remove confusing statement onupdated_at
matchingcompleted_at
whenstatus
isrefunded
(#1336)v2.9.1
: Allow anchors to omit the deprecatedX-Stellar-Signature
header (#1335)v2.9.0
: DeprecateX-Stellar-Signature
in favor ofSignature
(#1333)v2.8.0
: Addupdated_at
to transaction records. (#1329)v2.7.0
: Addrefund_memo
andrefund_memo_type
parameters to withdraw endpoint. (#1321)v2.6.3
: Clarifylang
defaults toen
when specified value is not supported. (#1320)v2.6.2
: Fixed the missing links of customer information needed. (#1316)v2.6.0
: Add callback signature and verification requirement. (#1263)v2.5.0
: Addexpired
transaction status. (#1233)v2.4.0
: Addrefunded
transaction status. (#1195)v2.3.0
: Changelang
format from ISO639-1 to RFC4646 which is a superset of ISO639-1. Addlang
field to GET/transactions
and/transaction
. (#1191)v2.2.1
: Makecompleted_at
field optional. (#1185)v2.2.0
: Deprecate refunded boolean. Add refund object to transaction records. (#1128)