Quotations
A Quote locks the exchange rate and fees for a transfer before
you commit to it. QuotationsClient wraps the two endpoints that create and read
quotations. Every order references exactly one quotation, and a quotation backs exactly
one order, forever β see the SDK overview for the full quote-to-order
walkthrough.
Reach these methods either directly on RetornaClient (client.createQuotation(...))
or through the sub-client (client.quotations().createQuotation(...)) β both call the
same code.
createQuotation(request)β
Prices a transfer for a given corridor and payout method, returning a rate you can act on before it expires.
Signatureβ
public QuotationDetail createQuotation(CreateQuotationRequest request)
Parametersβ
| Name | Type | Required | Description |
|---|---|---|---|
request | CreateQuotationRequest | Yes | The pricing request. See components below. |
CreateQuotationRequest components:
| Component | Type | Required | Description |
|---|---|---|---|
source | QuotationSource | Yes | Funding side. Carries a single currency (String) β the currency you fund from. |
destination | QuotationDestination | Yes | country (String, 2-char ISO 3166-1 alpha-2, uppercase), currency (String, destination currency), payoutMethod (QuotationPayoutMethod, required). |
destination.payoutMethod.type | PayoutMethodType | Yes | One of the platform's supported payout method types. Nested one level under destination.payoutMethod. |
quote | QuotationQuote | Yes | mode (QuoteMode: SEND_EXACT fixes the source amount, RECEIVE_EXACT fixes the target amount) and amount (String, decimal with up to 6 decimals, matching ^\d+(\.\d{1,6})?$). |
Returnsβ
QuotationDetail:
| Accessor | Type | Meaning |
|---|---|---|
id() | String | Quotation id. Pass this into CreateOrderRequest.quotationId to spend it. |
source() | QuotationSourceDetail | Source side: currency(), decimals() (int), grossAmount() (String, before fees), netAmount() (String, after fees). |
destination() | QuotationDestinationDetail | The corridor the quotation locked: country() (String, ISO-2) and payoutMethod().type() (PayoutMethodType). The order's payment instructions must match the payout type, or createOrder fails with B2B_QUOTATION_MISMATCH. Both are null only when the network could not attach a rate β do not build an order on that quotation. |
target() | QuotationTargetDetail | Target side: currency(), decimals() (int), amount() (String, what the beneficiary receives). |
exchangeRate() | ExchangeRate | value() (String), quotedAt() (OffsetDateTime), expiresAt() (OffsetDateTime). Check expiresAt() before spending the quotation. |
fees() | QuotationFees | totalInSourceCurrency() (String) and breakdown() (List<FeeBreakdownItem>). Each item has description(), pricingType() (PERCENTAGE or FIXED), appliedTo() (stage the fee applies to), value(), and currency(). |
JSON on the wire is snake_case (gross_amount, exchange_rate, total_in_source_currency,
...); the Java accessors above are camelCase β Jackson maps between the two for you.
Exampleβ
import app.retorna.sdk.RetornaClient;
import app.retorna.sdk.RetornaClientBuilder;
import app.retorna.sdk.core.environment.RetornaEnvironment;
import app.retorna.sdk.models.quotation.CreateQuotationRequest;
import app.retorna.sdk.models.quotation.PayoutMethodType;
import app.retorna.sdk.models.quotation.QuotationDestination;
import app.retorna.sdk.models.quotation.QuotationDetail;
import app.retorna.sdk.models.quotation.QuotationPayoutMethod;
import app.retorna.sdk.models.quotation.QuotationQuote;
import app.retorna.sdk.models.quotation.QuotationSource;
import app.retorna.sdk.models.quotation.QuoteMode;
RetornaClient client = new RetornaClientBuilder()
.environment(RetornaEnvironment.SANDBOX)
.clientId(System.getenv("RETORNA_CLIENT_ID"))
.clientSecret(System.getenv("RETORNA_CLIENT_SECRET"))
.privateKey(System.getenv("RETORNA_PRIVATE_KEY"))
.buildClient();
QuotationDetail quote = client.createQuotation(new CreateQuotationRequest(
new QuotationSource("USDR"),
new QuotationDestination("VE", "VES",
new QuotationPayoutMethod(PayoutMethodType.BANK_TRANSFER)),
new QuotationQuote(QuoteMode.SEND_EXACT, "10.00")));
System.out.printf("id=%s rate=%s expiresAt=%s%n",
quote.id(), quote.exchangeRate().value(), quote.exchangeRate().expiresAt());
System.out.printf("payout=%s β build the order with the matching instructions%n",
quote.destination().payoutMethod().type()); // P2P_PHONE_TRANSFER β BankPhoneAccount
System.out.printf("receiver gets %s %s (fees: %s %s)%n",
quote.target().amount(), quote.target().currency(),
quote.fees().totalInSourceCurrency(), quote.source().currency());
Errorsβ
IllegalArgumentExceptionβ thrown synchronously, before any request goes out, when a required field is missing or malformed (emptysource.currency, adestination.countrythat is not 2 uppercase letters, a missingquote.mode, or aquote.amountthat does not match the decimal pattern).RetornaB2BExceptionwithcodeEnum()one of:B2B_RATE_NOT_FOUNDβ no active rate for the requested corridor;B2B_UNAUTHORIZEDβ token or signature rejected;B2B_RATE_LIMITEDβ retryable, back off;B2B_PROVIDER_REJECTEDorB2B_PROVIDER_UNAVAILABLEβ the latter is retryable;B2B_UNKNOWNβ retry at most once. Calle.codeEnum().isRetryable()rather than hardcoding which codes to retry.RetornaAuthExceptionβ the OAuth2 token exchange itself failed.RetornaSdkExceptionβ network failure or a response that failed to deserialize.
Notesβ
- A quotation is single-use and time-limited: check
exchangeRate().expiresAt()before you build the order, and re-quote if it already passed. quote.amountmust be a decimal string. Passing adoubleor losing precision when building it is the most common way to get a confusing422.
API referenceβ
getQuotation(id)β
Reads back a previously created quotation by id β useful to re-check its expiry or terms before spending it.
Signatureβ
public QuotationDetail getQuotation(String id)
Parametersβ
| Name | Type | Required | Description |
|---|---|---|---|
id | String | Yes | The quotation id from a prior createQuotation() call. Validated for length only (1..100 chars) β treat it as an opaque string, not a UUID. |
Returnsβ
Same QuotationDetail shape returned by createQuotation above.
Exampleβ
import app.retorna.sdk.RetornaClient;
import app.retorna.sdk.RetornaClientBuilder;
import app.retorna.sdk.core.environment.RetornaEnvironment;
import app.retorna.sdk.models.quotation.QuotationDetail;
RetornaClient client = new RetornaClientBuilder()
.environment(RetornaEnvironment.SANDBOX)
.clientId(System.getenv("RETORNA_CLIENT_ID"))
.clientSecret(System.getenv("RETORNA_CLIENT_SECRET"))
.privateKey(System.getenv("RETORNA_PRIVATE_KEY"))
.buildClient();
QuotationDetail quote = client.getQuotation("0190f9b2-0000-7000-8000-000000000000");
System.out.printf("rate=%s expiresAt=%s%n",
quote.exchangeRate().value(), quote.exchangeRate().expiresAt());
Errorsβ
IllegalArgumentExceptionβidis null, empty, or longer than 100 characters.RetornaB2BExceptionwithcodeEnum()one of:B2B_QUOTATION_NOT_FOUNDβ does not exist or does not belong to you;B2B_UNAUTHORIZED;B2B_RATE_LIMITEDβ retryable;B2B_UNKNOWN. Checke.codeEnum().isRetryable()before retrying.RetornaAuthExceptionβ token exchange failure.RetornaSdkExceptionβ network failure or deserialization failure.
Notesβ
- The id is opaque. Do not assume it is a UUID even though the examples look like one β the backend does not promise a shape beyond "1 to 100 characters."