# Key concepts

The key concepts of the SDK are described below.

## Configuration

The `Config` class holds the different parameters required by the SDK.

You don't need to initialize your own `Config` objects. Use the `HarmonizeContext` to
load configuration, as shown in the following examples.

### Load configuration using default files


```java
var harmonizeCtx = HarmonizeContext.load(signer);
// we get the Ripple Custody instance from the context
var harmonize = harmonizeCtx.harmonize;
```

### Load configuration using a custom file


```java
var harmonizeCtx = HarmonizeContext.load(signer, configFileName);
// we get the Ripple Custody instance from the context
var harmonize = harmonizeCtx.harmonize;
```

For more information, see [Configure the SDK](/products/custody/get-started/integrate-via-sdk#configure-the-sdk).

## Context

The `Context` class represents an instance of Ripple Custody. To interact with the Ripple Custody API, you need to initialize a Ripple Custody context. There are two levels of context:

- Global application context `HarmonizeContext`.
- Local Ripple Custody context `Context`.


The global application context includes both of the following:

- The Ripple Custody `Config` instance.
- The `Harmonize` SDK instance.


The local Ripple Custody context groups together several sub-components needed by the SDK:

- The `Authenticator` instance, used to perform the OAuth authentication with Ripple Custody.
- The `Signer` instance, which abstracts the user credentials and signature implementation.
- The `Requests` instance, a helper class to make `HTTP` requests.
- The `Audit` instance, which can be used to add SDK HTTP audit trails.


## Instance

A `harmonize` instance groups together all API actions. It contains the local context, so it is meant for a specific configuration and a single user.
`harmonize` instances are lightweight and by themselves do not consume any resources.

Actions are the operations that can be performed on the API. They are accessed through the `harmonize` instance and grouped by API entity. For example, for domains, a `DomainActions` class exists, which is accessed through `harmonize.domains()`.

Each of the actions implements `Get`, `List`, `Update`, `Create`, `Lock`, and `Unlock` operations for its corresponding entity, if available in the API. For example, it is not possible to lock a transaction and therefore the `Lock` operation is not valid for the `TransactionActions` class.

This table lists the actions available from the `harmonize` instance:

| Method | Entity | Action class |
|  --- | --- | --- |
| `harmonize.tickers()` | Ticker | `TickerActions` |
| `harmonize.ledgers()` | Ledger | `LedgerActions` |
| `harmonize.genesis()` | GenesisRequest | `GenesisActions` |
| `harmonize.me()` | User (the current SDK user) | `MeActions` |
| `harmonize.domains()` | Domain | `DomainsActions` |
| `harmonize.requests(domainId)` | Request | `RequestActions` |
| `harmonize.intents(domainId)` | Intent | `IntentsActions` |
| `harmonize.vaults()` | Vault | `VaultsActions` |
| `harmonize.notarize()` | NotarizeData | `NotarizeActions` |
| `harmonize.transactions(domainId)` | ApiTransaction, ApiTransfer, TransactionOrder | `TransactionActions` |
| `harmonize.accounts(domainId)` | Account | `AccountsActions` |
| `harmonize.users(domainId)` | User | `UsersActions` |
| `harmonize.endpoints(domainId)` | Endpoint | `EndpointsActions` |
| `harmonize.policies(domainId)` | Policy | `PoliciesActions` |
| `harmonize.events(domainId)` | Event | `EventsActions` |


All actions implement a fully and partially fluent design.

### Fully fluent design

The fully fluent design flows from the action to entity creation and ends with a submit, which returns an `IntentData` instance.
The `IntentData` instance can be used to wait on request processing or poll the intent status later.

The flow is `{actions}` -> `{operation}` -> `{builder}` -> `{submit}` -> `{intentData}`:


```java
harmonize.domains().create()
                .id(id.toString())
                .alias(name)
                .permissions(Domains.Permissions.builder().readAccess(
                        Domains.ReadAccess.builder().all("admin").build()
                ).build())
                .policy(Policies.CreateDomainGenesisPolicy.buildDefault())
                .user(Users.CreateDomainGenesisUser.builder()
                        .alias(UUID.randomUUID().toString())
                        .description("Test user")
                        .customProperties(new HashMap<>())
                        .publicKey("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdJ0JuuWT4482+3liCAHj8Bx2vlCzam7spp8/RVaBphdvPDKtDN9PqVN5L12Qaiq14GmNxPxDXDWFbBV9vZ4xLg==")
                        .role("admin")
                        .build())
                .submit()
                .waitForSucceed(Timeout.minutes(1));
```

### Partially fluent design

The partially fluent design requires the creation of an object, which is subsequently passed to the operation and potentially reused.

The flow is `{builder}` -> `{build}` -> `{object}` and then `{actions}` -> `{operation}` <- `{object, intentData}`:


```java
var domain = Domains.CreateDomain.builder()
                .id(id.toString())
                .alias(name)
                .permissions(Domains.Permissions.builder().readAccess(
                        Domains.ReadAccess.builder().all("admin").build()
                ).build())
                .policy(Policies.CreateDomainGenesisPolicy.buildDefault())
                .user(Users.CreateDomainGenesisUser.builder()
                        .alias(UUID.randomUUID().toString())
                        .description("Test user")
                        .customProperties(new HashMap<>())
                        .publicKey("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdJ0JuuWT4482+3liCAHj8Bx2vlCzam7spp8/RVaBphdvPDKtDN9PqVN5L12Qaiq14GmNxPxDXDWFbBV9vZ4xLg==")
                        .role("admin")
                        .build())
                .build();

harmonize.domains().create(domain, Expire.hours(1)).waitForSucceed(Timeout.minutes(1));
```

### Action feedback polling

To maximize performance and responsiveness, the Ripple Custody API is fully asynchronous, so the default response only acknowledges reception of the request, but not whether it succeeded. You need to poll for the status of an action to check its processing status, which can be either `Failed`, `Processing`, or `Succeeded`.

The SDK offers helper methods to facilitate request monitoring. The SDK can wait for confirmation before processing further instructions when a request is sent.
This is enabled for both action design approaches and can be achieved using the following flow:

- `{actions}` -> `{operation}` -> `{submit}` -> `{wait-for-status}`, or
- `{actions}` -> `{operation, object}` -> `{wait-for-status}`


For example:


```java
harmonize.domains().create()
                .id(id.toString())
                ...
                .submit()
                .waitForProcessing(Timeout.minutes(1));
```

Or:


```java
harmonize.domains().create(domain, Expire.hours(24)).waitForProcessing(Timeout.minutes(1));
```

The `Expire` and `Timeout` arguments are not equivalent in the SDK:

- `Expire`: If the action sent is not approved within the `Expire` time, it is cancelled.
- `Timeout`: If a status, for example, `Succeeded`, is not detected within the timeout period, the SDK throws a timeout exception.


### Intent proposal requests

Requests that lead to the creation of intents are not executed immediately. They are passed to Ripple Custody as intent request proposals.

All SDK action classes return an `IntentData` instance when a proposal is sent to Ripple Custody.

The `IntentData` contains the following:

- Request ID
- Intent ID
- Proposal intent body `ProposeIntentBody`


We will now describe further the request and proposal.

## Request

When you perform an action with the SDK, you must check that the request itself succeeded. This does not mean the intent was executed; it only means that Ripple Custody accepted the request.

The request status can be one of the following:

| Request status | Description |
|  --- | --- |
| `Processing` | Ripple Custody is processing the request. |
| `Failed` | The request was rejected by the Ripple Custody for some reason. |
| `Succeeded` | Ripple Custody accepted the request, and the intent will be processed. Note that the intent itself may still fail. |


To wait for a specific request status, use a specific method. For example, to wait for a `Succeeded` status, use the following method:


```java
.waitForSucceed(Timeout.minutes(1));
```

If the user only requires acknowledgement that Ripple Custody received the request, use the following method:


```java
.waitForProcessingOrSucceeded(Timeout.minutes(1));
```

If the request `Failed` or polling timed out, a `RuntimeException` is thrown.

## Proposal

The intent goes through multiple phases indicated by its status, which can be one of the following:

| Intent status | Description |
|  --- | --- |
| `Open` | The intent was received and requires approval. |
| `Approved` | The intent was approved. |
| `Executed` | The intent was executed, for example, if the intent was to create a user, the user was created. |
| `Failed` | The intent failed due to some reason and was not `Executed`; a failed intent cannot be rejected or approved. |
| `Expired` | The intent expired before it was approved or rejected and will not be `Executed`. |
| `Rejected` | The intent was `Rejected` and will not be `Executed`. |


To wait on an intent status use:


```java
.waitForIntentStatus(Set.of(IntentStatus.Open, IntentStatus.Executed), Timeout.minutes(1));
```

Depending on the domain's policy, an intent proposal may need approval by one or more users.
It is important to note that if an intent requires one or more user approvals, that is, it requires human interaction,
the SDK user should not wait for the `Executed` status. A better approach is to poll the intent status using the `IntentsActions` at stages that make sense in the current application.

For example:


```java
Intents.State intentState = harmonize.intents(domainId).get(intentId).map(Intents.IntentEntity::getState).orElse(null);
```

All the `waitFor` methods will block the current thread and poll Ripple Custody for the intent status until the status is matched, a failure state is returned, or a timeout is reached.
A failure state or a timeout result in a `RuntimeException` thrown.

#### Transaction status

When using `TransactionActions` via `harmonize.transactions(domainId)`, the `TransactionIntentData` instance is returned.
This class has helper methods to wait for the different transaction statuses.

A transaction has a `ProcessingStatus` and a `LedgerTransactionStatus` status.

Remember that these methods block the current thread and should only be used when it makes sense for an application.

For example, to wait for a transaction to be completed, use:


```java
.waitForOrderTransactionProcessingStatus(Transactions.ProcessingStatus.Completed, Timeout.minutes(1));
```

### Intent approval or rejection

You can use the `IntentActions` instance to approve or reject an intent.

For example:


```java
harmonize.intents(domainId).approve(intentId)
        .waitForSucceed(Timeout.minutes(1));
```

You then need to check for the request status, as described in [Request](#request).

## Flexible JSON representation

All objects and entities passed to and from the API are in JSON format. The SDK maps JSON objects using the `MapJSON` class, from which all entities extend. This means that, at its most primitive layer, entities such as an account, domain, or user are all just simple JSON maps with keys and values.

The SDK provides convenient methods like `getId` that fetch the value for the key `id`. It is designed this way to provide maximum flexibility and backwards/forwards compatibility.

For example, the SDK can be used against API versions that return values the SDK does not yet support, and the operator can get that value using `get(Class class, String key)` or set a value using `set(String key, Object value)`.