Orders
An Order is the actual payout β it references a
quotation, carries the sender and receiver, and moves through the
states described in order status until it settles or
fails (see failure details). OrdersClient covers
creating, reading, and listing orders.
Reach these methods either directly on RetornaClient or through
client.orders() β both call the same code.
createOrder(request, idempotencyKey)β
Creates an order against a quotation. The backend requires an
X-Idempotency-Key header on every call; the SDK sends it for you from the
idempotencyKey argument.
Signatureβ
public Order createOrder(CreateOrderRequest request)
public Order createOrder(CreateOrderRequest request, String idempotencyKey)
The single-argument overload generates a random UUID as the idempotency key. Prefer the
two-argument form with your own reference (an invoice number, for example) β that value
round-trips as order.idempotencyKey(), so you can look the order up later by it, and a
retry that reuses the same key against the same body replays the original order instead
of creating a duplicate.
Parametersβ
| Name | Type | Required | Description |
|---|---|---|---|
request | CreateOrderRequest | Yes | The order to create. See components below. |
idempotencyKey | String | No | Your own reference. When null or blank, a random UUID is generated. Reusing the same key with a different body is a 409; reusing it with the identical body replays the original order. |
CreateOrderRequest components β build it with the canonical constructor or
withPurpose(...):
// Canonical (5-arg) constructor
public CreateOrderRequest(
String quotationId,
Map<String, Object> metadata,
String purpose,
OrderSender sender,
OrderDestination destination
)
// Typed entry point for the purpose catalog
public static CreateOrderRequest withPurpose(
String quotationId,
Map<String, Object> metadata,
OrderPurpose purpose,
OrderSender sender,
OrderDestination destination
)
| Component | Type | Required | Description |
|---|---|---|---|
quotationId | String | Yes | UUID of a previously-created quotation. Single-use: it backs exactly one order, forever. |
metadata | Map<String, Object> | No | Client-defined labels, echoed back untouched. Omitted from the wire when null. |
purpose | String (or OrderPurpose via withPurpose) | Yes | Declared remittance purpose. Must be a value from OrderPurpose β null or anything outside the catalog throws client-side. There is no idempotencyKey field here; it is response-only. |
sender | OrderSender | Yes | names, lastNames, document (PersonDocument), country (2-char ISO alpha-2), phone (nullable, E.164), email (nullable). |
destination | OrderDestination | Yes | See below. |
OrderSender is a record: OrderSender(names, lastNames, document, country, phone, email). The
old 4-arg constructor (names, lastNames, document, country) still works β phone and email
default to null β so existing code compiles unchanged. Pass the 6-arg form to set phone
and/or email; the client validates phone as E.164 and email as an address when non-null.
Both round-trip in sender() on reads only when you sent them on create.
OrderDestination β for building a request, use the 2-arg constructor
(receiver, paymentInstructions); targetAmount/targetCurrency are response-only
and stay off the create wire.
| Component | Type | Required | Description |
|---|---|---|---|
receiver | OrderReceiver | Yes | names, lastNames, document (PersonDocument), email (nullable), country (2-char ISO alpha-2). |
paymentInstructions | PaymentInstructions | Yes | Exactly one of bankAccount or bankPhoneAccount must be set β build it with PaymentInstructions.ofBankAccount(...) or PaymentInstructions.ofBankPhoneAccount(...). |
PersonDocument(type, id, country) β type (e.g. "CC", "V"), id
(document number), country (2-char ISO alpha-2 issuing country).
For a bank-transfer payout, build the account with BankAccount.of(FinancialEntity, number, BankAccountType):
public static BankAccount of(FinancialEntity bank, String number, BankAccountType type)
| Component | Type | Required | Description |
|---|---|---|---|
bank | FinancialEntity | Yes | Platform code from the closed bank catalog, e.g. BANCO_BANESCO_VE β never the commercial name. See bank entities for the full list. |
number | String | Yes | Account number, kept as a string to preserve leading zeros. |
type | BankAccountType | Yes | CHECKING, SAVINGS, CORRIENTE, or AHORRO β the backend accepts both the English and Spanish forms. |
Returnsβ
Order:
| Accessor | Type | Meaning |
|---|---|---|
id() | String | Order id. |
type() | String | Order type (e.g. "REMITTANCE"). |
status() | String | See order status for the observed values and how they evolve. |
quotationId() | String | The quotation this order was created from. |
idempotencyKey() | String | Your idempotency key, echoed back; nullable. |
amount() / currency() | String | Source-currency amount and code. |
metadata() | Map<String, Object> | Echoed metadata; nullable. |
sender() / destination() | OrderSender / OrderDestination | Nullable in some states. destination().targetAmount()/targetCurrency() carry the payout-side amount on reads. |
execution() | OrderExecution | source() and destination(), each an ExecutionLeg with status() and referenceId() (nullable). |
purposeEnum() | OrderPurpose | Parsed from the raw purpose() string; null if absent or unrecognized by this SDK version. |
events() | List<OrderEvent> | Status-change timeline; nullable. Each OrderEvent has fromStatus() (nullable), toStatus(), trigger(), reason() (nullable), message() (nullable), createdAt(). |
failureCode() / failureReason() | String | Machine-readable / human-readable failure detail; both nullable. See failure details. Branch on failureCode(), never on failureReason(). |
createdAt() / updatedAt() | OffsetDateTime | Timestamps. |
JSON on the wire is snake_case (quotation_id, idempotency_key, failure_code, ...); the
Java accessors above are camelCase.
Exampleβ
import app.retorna.sdk.RetornaClient;
import app.retorna.sdk.RetornaClientBuilder;
import app.retorna.sdk.core.environment.RetornaEnvironment;
import app.retorna.sdk.models.order.BankPhoneAccount;
import app.retorna.sdk.models.order.CreateOrderRequest;
import app.retorna.sdk.models.order.FinancialEntity;
import app.retorna.sdk.models.order.Order;
import app.retorna.sdk.models.order.OrderDestination;
import app.retorna.sdk.models.order.OrderPurpose;
import app.retorna.sdk.models.order.OrderReceiver;
import app.retorna.sdk.models.order.OrderSender;
import app.retorna.sdk.models.order.PaymentInstructions;
import app.retorna.sdk.models.order.PersonDocument;
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();
String quotationId = "0190f9b2-0000-7000-8000-000000000000"; // from createQuotation
String idempotencyKey = "invoice-2026-0042"; // your own reference
Order order = client.createOrder(
CreateOrderRequest.withPurpose(
quotationId,
null,
OrderPurpose.FAMILY_SUPPORT,
new OrderSender("Ana MarΓa", "PΓ©rez GΓ³mez",
new PersonDocument("CC", "1020304050", "CO"), "CO"),
new OrderDestination(
new OrderReceiver("Carlos AndrΓ©s", "RodrΓguez LΓ³pez",
new PersonDocument("V", "12345678", "VE"),
"[email protected]", "VE"),
PaymentInstructions.ofBankPhoneAccount(
BankPhoneAccount.of(FinancialEntity.BNC_VE,
"+584121234567", "12345678")))),
idempotencyKey);
System.out.printf("Order %s status=%s idempotencyKey=%s%n",
order.id(), order.status(), order.idempotencyKey());
Errorsβ
IllegalArgumentExceptionβ thrown before any request goes out, for a malformedquotationId(not a UUID), a null or unrecognizedpurpose, missing sender/receiver fields, a receiver email that fails a basic shape check, a non-nullsender.phonethat is not E.164, a non-nullsender.emailthat fails a basic shape check, or payment instructions that set both or neither ofbankAccount/bankPhoneAccount.RetornaB2BExceptionwithcodeEnum()one of:B2B_QUOTATION_NOT_FOUND,B2B_QUOTATION_EXPIRED,B2B_QUOTATION_MISMATCH,B2B_QUOTATION_ALREADY_USEDβ read the note on that page before retrying anything,B2B_INSUFFICIENT_BALANCE,B2B_BENEFICIARY_INVALID,B2B_IDEMPOTENCY_CONFLICTβ same key, different body,B2B_UNAUTHORIZED,B2B_RATE_LIMITEDβ retryable with the same key,B2B_PROVIDER_REJECTEDorB2B_PROVIDER_UNAVAILABLEβ the latter retryable,B2B_UNKNOWN. Usee.codeEnum().isRetryable()to decide, and always retrycreateOrderwith the exact sameidempotencyKey.RetornaAuthExceptionβ token exchange failure.RetornaSdkExceptionβ network failure or deserialization failure.
Notesβ
- Always pass your own
idempotencyKeyin production code; the single-argument overload is convenient for scripts, not for anything you might need to safely retry. - There is no
idempotencyKeyfield on the request β only on the response. Do not try to set one; it always comes from the idempotency key.
API referenceβ
getOrder(id)β
Reads a single order by id.
Signatureβ
public Order getOrder(String id)
Parametersβ
| Name | Type | Required | Description |
|---|---|---|---|
id | String | Yes | The order id as the backend issued it β validated for length only (1..100 chars), not a UUID shape. |
Returnsβ
Same Order shape returned by createOrder above.
Exampleβ
import app.retorna.sdk.RetornaClient;
import app.retorna.sdk.RetornaClientBuilder;
import app.retorna.sdk.core.environment.RetornaEnvironment;
import app.retorna.sdk.models.order.Order;
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();
Order order = client.getOrder("0190f9b2-0000-7000-8000-000000000001");
System.out.printf("status=%s failureCode=%s%n", order.status(), order.failureCode());
Errorsβ
IllegalArgumentExceptionβidis null, empty, or longer than 100 characters.RetornaB2BExceptionwithcodeEnum()one of:B2B_ORDER_NOT_FOUND,B2B_UNAUTHORIZED,B2B_RATE_LIMITEDβ retryable,B2B_UNKNOWN.RetornaAuthExceptionβ token exchange failure.RetornaSdkExceptionβ network failure or deserialization failure.
Notesβ
- The id is opaque; do not assume a fixed shape.
API referenceβ
listOrders(params)β
Lists orders, cursor-paginated, with optional filters.
Signatureβ
public OrdersListResponse listOrders(ListOrdersParams params)
Parametersβ
| Name | Type | Required | Description |
|---|---|---|---|
params | ListOrdersParams | No (pass null or ListOrdersParams.firstPage()) | Query filters. See components below. |
ListOrdersParams components:
| Component | Type | Required | Description |
|---|---|---|---|
cursor | String | No | Opaque cursor from the previous page's next_cursor. Null for the first page. |
limit | Integer | No | Page size, 1..100; backend default is 20. Keep it fixed across a walk. |
status | String | No | Filter by order status. Free-form β not constrained to an enum. |
idempotencyKey | String | No | Filter by your idempotency key. Exact match, not partial. |
createdFrom / createdTo | OffsetDateTime | No | Inclusive bounds on created_at. |
type | String | No | Filter by transaction type, e.g. "WALLET_TO_POG". Free-form, a single value β unlike status, it does not take a comma-separated list. Added as the last component of ListOrdersParams; the old 6-arg constructor still works and leaves type null. |
Convenience factories: ListOrdersParams.firstPage(), firstPage(int limit),
byIdempotencyKey(String idempotencyKey), and byType(String type).
Returnsβ
OrdersListResponse:
| Accessor | Type | Meaning |
|---|---|---|
data() | List<Order> | Orders on this page β see the Order shape from createOrder above. |
pagination() | CursorPagination | nextCursor() (String, nullable when done), hasMore() (boolean), limit() (int), total() (Integer, nullable β count of matches, not page size). |
Exampleβ
import app.retorna.sdk.RetornaClient;
import app.retorna.sdk.RetornaClientBuilder;
import app.retorna.sdk.core.environment.RetornaEnvironment;
import app.retorna.sdk.models.order.ListOrdersParams;
import app.retorna.sdk.models.order.Order;
import app.retorna.sdk.models.order.OrdersListResponse;
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();
String cursor = null;
do {
OrdersListResponse page = client.listOrders(
cursor == null ? ListOrdersParams.firstPage(20)
: new ListOrdersParams(cursor, 20, null, null, null, null));
for (Order order : page.data()) {
System.out.printf("%s %s %s%n", order.id(), order.status(), order.amount());
}
cursor = page.pagination().nextCursor();
} while (cursor != null);
Errorsβ
IllegalArgumentExceptionβ an empty (but non-null)cursororidempotencyKey, or alimitoutside 1..100.RetornaB2BExceptionwithcodeEnum()one of:B2B_INVALID_PAGINATION_CURSORβ restart the walk without a cursor,B2B_UNAUTHORIZED,B2B_RATE_LIMITEDβ retryable,B2B_UNKNOWN.RetornaAuthExceptionβ token exchange failure.RetornaSdkExceptionβ network failure or deserialization failure.
Notesβ
- See pagination for the full cursor contract: keep
limitand filters fixed for the whole walk, and treat the cursor as opaque. idempotencyKeyis an exact match on your idempotency key β often faster than paging when you already know which order you are looking for.