Skip to main content

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​

NameTypeRequiredDescription
requestCreateQuotationRequestYesThe pricing request. See components below.

CreateQuotationRequest components:

ComponentTypeRequiredDescription
sourceQuotationSourceYesFunding side. Carries a single currency (String) β€” the currency you fund from.
destinationQuotationDestinationYescountry (String, 2-char ISO 3166-1 alpha-2, uppercase), currency (String, destination currency), payoutMethod (QuotationPayoutMethod, required).
destination.payoutMethod.typePayoutMethodTypeYesOne of the platform's supported payout method types. Nested one level under destination.payoutMethod.
quoteQuotationQuoteYesmode (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:

AccessorTypeMeaning
id()StringQuotation id. Pass this into CreateOrderRequest.quotationId to spend it.
source()QuotationSourceDetailSource side: currency(), decimals() (int), grossAmount() (String, before fees), netAmount() (String, after fees).
destination()QuotationDestinationDetailThe 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()QuotationTargetDetailTarget side: currency(), decimals() (int), amount() (String, what the beneficiary receives).
exchangeRate()ExchangeRatevalue() (String), quotedAt() (OffsetDateTime), expiresAt() (OffsetDateTime). Check expiresAt() before spending the quotation.
fees()QuotationFeestotalInSourceCurrency() (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 (empty source.currency, a destination.country that is not 2 uppercase letters, a missing quote.mode, or a quote.amount that does not match the decimal pattern).
  • RetornaB2BException with codeEnum() 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_REJECTED or B2B_PROVIDER_UNAVAILABLE β€” the latter is retryable; B2B_UNKNOWN β€” retry at most once. Call e.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.amount must be a decimal string. Passing a double or losing precision when building it is the most common way to get a confusing 422.

API reference​

POST /quotations

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​

NameTypeRequiredDescription
idStringYesThe 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 β€” id is null, empty, or longer than 100 characters.
  • RetornaB2BException with codeEnum() one of: B2B_QUOTATION_NOT_FOUND β€” does not exist or does not belong to you; B2B_UNAUTHORIZED; B2B_RATE_LIMITED β€” retryable; B2B_UNKNOWN. Check e.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."

API reference​

GET /quotations/:id